FFT太玄幻了,不过我要先膜拜HQM,实在太强了

1.多项式

1)多项式的定义

在数学中,由若干个单项式相加组成的代数式叫做多项式。多项式中的每个单项式叫做多项式的项,这些单项式中的最高项次数,就是这个多项式的次数。其中多项式中不含字母的项叫做常数项。

2)多项式的表达

我们可以用一些不同的表达方式来表示一个多项式

\[f(x)=\sum_{i=0}^{i=n}a_i\cdot x^i
\]

系数表达:

可以用一个n+1维的向量来表示

\[\vec{a}=(a_0,a_1,\cdots,a_n)
\]

点值表达:

我们要选取任意\(n+1\)个点值\(x_0,...,x_n\)求出它的\(f(x_i)\),得到$${(x_i,f(x_i)):0<=i<=n,i∈Z} $$

我们可以把\(n\)次多项式\(A(x)\)看作一个函数,那么它可以用平面直角坐标系上的n个点\((x_1,y_1),(x_2,y_2),...,(x_n,y_n)\)来确定。这里我们可以直观理解一下:两个点我们可以轻易确定一条直线,同时我们依然可以由三个点确定一条抛物线,这个是可以证明的,MYH巨佬给我们讲过,但是我太菜了,实在没有听懂。回到上面,我们把这n个点代入\(A(x)\),我们就可以得到一个\(n\)元一次方程组,然后通过高斯消元就可以确定这个多项式。

系数表达法的多项式乘法时间复杂度显然是\(O(n^2)\)的,但是点值表达法的多项式的乘法的时间复杂度却是\(O(n\))的(两个多项式的每一个点的横坐标都相等)。那我们就会希望可以利用点值表达法的这一优势来快速地进行多项式乘法。但是我们平时使用的都是系数表示法,想要利用这个优势,就要快速地将系数表达法转化为点值表达法。这里我们就要用FFT了。

2.复数

1)复数的定义

我们把形如\(a+bi\)(a,b均为实数)的数称为复数,其中\(a\)称为实部,\(b\)称为虚部,i称为虚数单位,也就是说\(i^2=-1\),或者说\(i=\sqrt{-1}\)。当虚部等于零时,这个复数可以视为实数;当z的虚部不等于零时,实部等于零时,常称z为纯虚数。复数域是实数域的代数闭包,也即任何复系数多项式在复数域中总有根。 复数是由意大利米兰学者卡当在十六世纪首次引入,经过达朗贝尔、棣莫弗、欧拉、高斯等人的工作,此概念逐渐为数学家所接受。

2)复数的四则运算

复数的加法

设\(z_1=a+bi,z_2=c+di\),那么:$$z_1\pm z_2=(a+c)+(b+d)i$$

复数的乘法

设\(z_1=a+bi,z_2=c+di\),那么:$$z_1\times z_2=(ac-bd)+(ad+bc)i$$

一个奇奇怪怪的东西

\[e^{i\theta}=cos\theta+isin \theta
\]

单位复数根

定义

如果\(\omega^n=1\),那么我们称\(\omega\)为\(n\)次单位根。

单位根的值

事实上,在这里:$$\omega_k=e^\frac{2\pi ik}{n}=cos\frac{2\pi k}{n}+isin\frac{2\pi k}{n}$$

这个可以用欧拉恒等式\((e^{i\pi}+1=0)\)来轻易证明。

消去引理

\[\omega_{dn}^{dk}=\omega_n^k
\]

证明:

\[\omega_{dn}^{dk}=(e^\frac{2\pi i}{dn})^{dk}=e^\frac{2\pi ik}{n}=\omega_n^k
\]

折半引理

如果n是大于0的一个偶数,那么前\frac{n}{2}个\(n\)次单位根的平方的集合等于后\frac{n}{2}个n次单位根的平方的集合。

证明:对于任意的k<\frac{n}{2},有:

\[(\omega_n^{k+\frac{n}{2}})^2=\omega_n^{2k+n}=\omega_n^{2k}\omega_n^n=\omega_n^{2k}=(\omega_n^k)^2
\]

因此,$$(\omega_n{k+\frac{n}{2}})2=(\omega_nk)2$$

复数类

struct cmplx{
double real,imag;
inline cmplx(double x=0,double y=0){real=x,imag=y;}
inline cmplx operator +(const int b){return cmplx(real+b,imag);}
inline cmplx operator -(const int b){return cmplx(real-b,imag);}
inline cmplx operator *(const int b){return cmplx(real*b,imag*b);}
inline cmplx operator /(const int b){return cmplx(real/b,imag/b);}
inline cmplx operator+=(const int b){*this=*this+b;return *this;}
inline cmplx operator/=(const int b){*this=*this/b;return *this;}
inline cmplx operator +(const cmplx b){return cmplx(real+b.real,imag+b.imag);}
inline cmplx operator -(const cmplx b){return cmplx(real-b.real,imag-b.imag);}
inline cmplx operator *(const cmplx b){return cmplx(real*b.real-imag*b.imag,real*b.imag+imag*b.real);}
inline cmplx operator+=(const cmplx b){*this=*this+b;return *this;}
inline cmplx operator*=(const cmplx b){*this=*this*b;return *this;}
};

FFT(fast Fourier transform)

我们正式开始我们的FFT之旅。

FFT就是用来快速求傅里叶变换

证明

终于切入正题了。

DFT就是将一个多项式的系数表达法转化为点值表达法。

具体操作:

对于n-1(2|n)次多项式A(x),我们有:

\[A(x)=a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1}
\]

令$$A{[1]}=a_0+a_2x2+...+a_{n-2}x^{\frac{n}{2}-1}$$

\[A^{[2]}=a_1+a_3x+...+a_{n-1}x^{\frac{n}{2}-1}
\]

则$$A(x)=A{[1]}(x2)+xA{[2]}(x2)$$

把\(\omega_n^k(k<\frac{n}{2})\)代入可得到:$$A(\omega_nk)=A{[1]}((\omega_nk)2)+\omega_nkA{[2]}((\omega_nk)2)$$

由折半引理我们会发现\((\omega_n^k)^2=(\omega_n^{k+\frac{n}{2}})^2\)

易证\(\omega_n^k=-\omega_n^{k+\frac{n}{2}}\)

所以$$A(\omega_n{k+\frac{n}{2}})=A{[1]}((\omega_nk)2)-\omega_nkA{[2]}((\omega_nk)2)$$

我们会发现这两个式子只有常数项不同

那么当我们在求第一个式子的时候,我们可以直接在O(1)的时间内同时得到第二个式子的值

然后就这样递归下去,就可以得到A(x)的点值表达式了

这里先埋个坑吧,证明真的不想肝啊【绝望脸】

直接上代码吧

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath> using namespace std; const double pi=acos(-1); struct cmplx{
double real,imag;
inline cmplx(double x=0,double y=0){real=x,imag=y;}
inline cmplx operator +(const int b){return cmplx(real+b,imag);}
inline cmplx operator -(const int b){return cmplx(real-b,imag);}
inline cmplx operator *(const int b){return cmplx(real*b,imag*b);}
inline cmplx operator /(const int b){return cmplx(real/b,imag/b);}
inline cmplx operator+=(const int b){*this=*this+b;return *this;}
inline cmplx operator/=(const int b){*this=*this/b;return *this;}
inline cmplx operator +(const cmplx b){return cmplx(real+b.real,imag+b.imag);}
inline cmplx operator -(const cmplx b){return cmplx(real-b.real,imag-b.imag);}
inline cmplx operator *(const cmplx b){return cmplx(real*b.real-imag*b.imag,real*b.imag+imag*b.real);}
inline cmplx operator+=(const cmplx b){*this=*this+b;return *this;}
inline cmplx operator*=(const cmplx b){*this=*this*b;return *this;}
}; cmplx a[10000001],b[10000001];
int n,m,limit,res[10000001]; void fft(cmplx *a,int t){
for(int i=0;i<n;i++){
if(i<res[i])swap(a[i],a[res[i]]);
}
for(int i=1;i<n;i<<=1){
cmplx wn(cos(pi/i),t*sin(pi/i));
for(int p=i<<1,j=0;j<n;j+=p){
cmplx w(1,0);
for(int k=0;k<i;k++,w*=wn){
cmplx x=a[j+k],y=w*a[j+k+i];
a[j+k]=x+y;a[j+k+i]=x-y;
}
}
}
} int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++){
scanf("%lf",&a[i].real);
}
for(int i=0;i<=m;i++){
scanf("%lf",&b[i].real);
}
m+=n;
for(n=1;n<=m;n<<=1)limit++;
for(int i=0;i<n;i++){
res[i]=((res[i>>1]>>1)|(i&1)<<(limit-1));
}
fft(a,1);
fft(b,1);
for(int i=0;i<=n;i++)a[i]*=b[i];
fft(a,-1);
for(int i=0;i<=m;i++){
printf("%d ",int(a[i].real/n+0.5));
}
return 0;
}

FFT快速傅里叶变换的更多相关文章

  1. FFT 快速傅里叶变换 学习笔记

    FFT 快速傅里叶变换 前言 lmc,ikka,attack等众多大佬都没教会的我终于要自己填坑了. 又是机房里最后一个学fft的人 早背过圆周率50位填坑了 用处 多项式乘法 卷积 \(g(x)=a ...

  2. CQOI2018 九连环 打表找规律 fft快速傅里叶变换

    题面: CQOI2018九连环 分析: 个人认为这道题没有什么价值,纯粹是为了考算法而考算法. 对于小数据我们可以直接爆搜打表,打表出来我们可以观察规律. f[1~10]: 1 2 5 10 21 4 ...

  3. 「学习笔记」FFT 快速傅里叶变换

    目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...

  4. FFT —— 快速傅里叶变换

    问题: 已知A[], B[], 求C[],使: 定义C是A,B的卷积,例如多项式乘法等. 朴素做法是按照定义枚举i和j,但这样时间复杂度是O(n2). 能不能使时间复杂度降下来呢? 点值表示法: 我们 ...

  5. [C++] 频谱图中 FFT快速傅里叶变换C++实现

    在项目中,需要画波形频谱图,因此进行查找,不是很懂相关知识,下列代码主要是针对这篇文章. http://blog.csdn.net/xcgspring/article/details/4749075 ...

  6. matlab中fft快速傅里叶变换

    视频来源:https://www.bilibili.com/video/av51932171?t=628. 博文来源:https://ww2.mathworks.cn/help/matlab/ref/ ...

  7. FFT快速傅里叶变换算法

    1.FFT算法概要: FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法.即为快速傅氏变换.它是根据离散傅氏变换的奇.偶.虚.实等特性,对离散傅立叶变换 ...

  8. [学习笔记]FFT——快速傅里叶变换

    大力推荐博客: 傅里叶变换(FFT)学习笔记 一.多项式乘法: 我们要明白的是: FFT利用分治,处理多项式乘法,达到O(nlogn)的复杂度.(虽然常数大) FFT=DFT+IDFT DFT: 本质 ...

  9. FFT(快速傅里叶变换)

    学习了FFT用来求多项式的乘法,看了算导上的介绍,上面讲的非常明白,概括一下FFT的原理就是,我们在计算多项式的乘法时,如果暴力模拟的话是n^2 复杂度的,就像小学学的竖式乘法一样,比如一个n位数乘上 ...

随机推荐

  1. WMI 技术

    什么是WMI? Windows Management Instrumentation (WMI)是可伸缩的系统管理结构,该规范采用一个统一.基于标准且可扩展的面向对象接口.它提供与系统管理员信息和基础 ...

  2. Poj 3177 Redundant Paths (双连通分支+节点统计)

    题目描述: 给出一个无向的连通图,问最少加入几条边,才能使所给的图变为无桥的双连通图? 解题思路: 可以求出原图中所有的不包含桥的所有最大连通子图,然后对连通子图进行标记缩点,统计度为1的叶子节点le ...

  3. 贪心+构造 Codeforces Round #277 (Div. 2) C. Palindrome Transformation

    题目传送门 /* 贪心+构造:因为是对称的,可以全都左一半考虑,过程很简单,但是能想到就很难了 */ /************************************************ ...

  4. Linux下磁盘分区、挂载、卸载操作记录

    Linux下磁盘分区.挂载.卸载操作记录. 操作环境:CentOS release 6.5 (Final) Last :: from 118.230.194.76 [root@CentOS ~]# [ ...

  5. ViewPager(4)用viewpager实现splash view

    1,示例 2,代码 SplashMain.java import android.os.Bundle; import android.support.v4.app.Fragment; import a ...

  6. Jayway JsonPath实例

    开源:https://github.com/json-path/JsonPath 引入库: <dependency> <groupId>com.jayway.jsonpath& ...

  7. 网站开发综合技术 一 JavaScript简介 二JavaScript语法

    第1部分 JavaScript简介 1.JavaScript它是个什么东西? 它是个脚本语言,需要有宿主文件,他的宿主文件是html文件. 2.它与Java有什么关系? 没有什么直接联系,java是S ...

  8. C#与正则表达式的例子

    一个很好的文章,但是并没有测试 连接

  9. linux创建ftp用户以及指定目录问题

    linux创建ftp用户以及指定目录问题 创建用户命令:如我的目录是根目录下的 MyWeb 用户名:xdh2571 #useradd -G ftp -d /MyWeb -M xdh2571#passw ...

  10. Spark学习之键值对(pair RDD)操作(3)

    Spark学习之键值对(pair RDD)操作(3) 1. 我们通常从一个RDD中提取某些字段(如代表事件时间.用户ID或者其他标识符的字段),并使用这些字段为pair RDD操作中的键. 2. 创建 ...