前言
基本没写过笔记这种东西,但是FFT这东西一开始就摧毁了我的智商,写写加深印象吧。
多亏了yhzq+yveh的笔记帮助,总算是明白一些了,以下部分内容引自二位dalao
前置知识
多项式定义
以 x x 为变量的多项式是定义在一个代数域上,将函数 A(x) A ( x ) 表示为形式和
我们称 a0,a1,a2⋯an−1 a 0 , a 1 , a 2 ⋯ a n − 1 为多项式的系数。所有的系数都属于F,比如复数域C。
如果一个多项式 A(x) A ( x ) 的最高次项系数为 ak a k ,那我们称 A(x) A ( x ) 的次数为 k k ,记作
多项式运算
单点求值
这个数学老师也告诉我们啦,秦九韶公式嘛
多项式加法
这个比较好想啦,就是次数相同的每一项相加
多项式乘法
就是将
A(x)
A
(
x
)
中的每一项与
B(x)
B
(
x
)
中的每一项相乘,然后同类项合并,举个栗子
差不多是高精乘的影子了
显然 degree(C)=degree(A)+degree(B) d e g r e e ( C ) = d e g r e e ( A ) + d e g r e e ( B )
多项式表示
多项式的表示方法有两种:系数表示,点值表示
系数表示的加法是
O(n)
O
(
n
)
,乘法却是
O(n2)
O
(
n
2
)
的
那么点值表示是什么呢?从函数的角度考虑,对于一次函数,两点可以确定,那么只要我们用n个不同的点来表示的话
多项式也是唯一的
加法的时候就是把每个点加上
复杂度 O(n) O ( n )
乘法呢?
因为
degree(C)=degree(A)+degree(B)
d
e
g
r
e
e
(
C
)
=
d
e
g
r
e
e
(
A
)
+
d
e
g
r
e
e
(
B
)
,此时我们必须找到2n个点表示多项式,所以需要对
A,B
A
,
B
的点值拓展,给定A的拓展式:
再给出B的:
这样可以得到C:
也是优越的O(n)
复数
基本知识
复数,为实数的延伸,它使任一多项式方程式都有根。通常表示为 a+bi a + b i ,a,b为实数,且 −1−−−√=i − 1 = i ,a为它的实部,b为它的虚部
复平面,是x轴表示实数与y轴(除了原点)表示虚数,建立起来的复数的几何表示。
每一个复数
a+bi
a
+
b
i
都能表示为一个
(0,0)
(
0
,
0
)
指向
(a,b)
(
a
,
b
)
的向量。
引理2—–消去引理
引理3—–折半引理
还有引理4—–求和引理
FFT
思想
终于到FFT了,它做了一件什么事呢?
系数表达–>点值表达–>计算乘法–>点值表达–>系数表达,运用了高效的插值使总时间复杂度为O(nlogn)
过程
DFT
系数表达–>点值表达
为了方便,下面的n都是2^x,不足的把系数用0补
FFT计算
IDFT
实现方法
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=300005;
const double pi=acos(-1.0);
struct complex
{
double x,y;
complex(double X=0,double Y=0){x=X; y=Y;}
}a[N],b[N];
int n,m,L,r[N];
complex operator +(complex a,complex b){return complex(a.x+b.x,a.y+b.y);}
complex operator -(complex a,complex b){return complex(a.x-b.x,a.y-b.y);}
complex operator *(complex a,complex b){return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
void FFT(complex *a,int id)
{
for (int i=0;i<n;i++)
if (i<r[i]) swap(a[i],a[r[i]]);
for (int k=1;k<n;k<<=1)
{
complex wn=complex(cos(pi/k),id*sin(pi/k));
for (int i=0;i<n;i+=(k<<1))
{
complex w=complex(1,0);
for (int j=0;j<k;j++,w=w*wn)
{
complex x=a[i+j],y=w*a[i+j+k];
a[i+j]=x+y; a[i+j+k]=x-y;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=0;i<=n;i++) scanf("%lf",&a[i].x);
for (int i=0;i<=m;i++) scanf("%lf",&b[i].x);
m+=n;
for (n=1;n<=m;n<<=1) ++L;
for (int i=0;i<n;i++)
r[i]=(r[i>>1]>>1) | ((i&1)<<L-1);
FFT(a,1); FFT(b,1);
for (int i=0;i<=n;i++) a[i]=a[i]*b[i];
FFT(a,-1);
for (int i=0;i<=m;i++) printf("%d ",(int)(a[i].x/n+0.5));
}