Discrete Fourier transform DFT
离散傅里叶变换 ——一种过程
InverseDiscreteFourierTransform IDFT
离散傅里叶逆变换
Fast Fourier Transformation FFT
快速傅里叶变换 ——一种算法
FFT 在ACM 中解决啥呢
多项式乘法
A*B=C
A = a0 + a1 x^1 + a2 x^2 + a(n-1) x^(n-1)
B = b0 + ...
C = c0 + ... +c(n-1) x^(n-1) + cn x^n + ... +c(2n-1) x^(2n-1)
计算需要
把 A 和 B 都添加 n 个 系数为 0 的项 —— 0 x^n 0 x^(n+1) .. 0 ^x(2n-1)
我们知道
朴素算法,两两相乘,求出 c0 to c2(n-1) 需要 O(n^2)的时间
而利用 傅里叶 什么的 可以 达到 O(n log n )
听起来不可思议……
我们知道
对于一组
A = a0 + a1 x^1 + a2 x^2 + a(n-1) x^(n-1)
有如下表示方法
1、向量 ( a0 , a1 , a2 ... a(n-1) )
2、神奇的点表示法
如果视 A 的系数 a0 to a(n-1) 为 n 个未知量
我需要多少组 x 能解出他们呢?
答案是 n 组
所以:【可以用 n 个 A(xi)=yi 】来表示 这个向量
又一个神奇的事情发生了——
如果
我知道 A 的 点值表达是 A(xi)=ya i
也知道 B 的 点值表达是 B(xi)=yb i
那么 C 的 点值 可以O(n)地 知道 是 C(xi) = yai*ybi
PS: A(xi)=ya i B(xi)=yb i
So A(xi)*B(xi) = ya * yb
而 C(xi)=A(xi)*B(xi)
So C(xi) = yai*ybi
所以
用某种O(nlogn)的方法 求出 A和B 的点值表达 —— DFT ①
就可以继续 O(n) 地求出 C 的点值表达 —— 逐点相乘
然后再用某种O(nlogn)的算法 根据C的点值表达逆推出C的向量表达 ——IDFT ②
就成功地用 O(nlogn) 的时间 解决了问题 ——FFT
① DFT
首先 A和B的点值表达 是由我们来构造的
我们选择某组神奇 的 xi (i 1to n) 来 求出 对应的 yi 即可
怎么选呢——
n次单位复数根
满足 wn^n=1 的 wn n次单位复数根 恰好是n个
e^(2πki/n) k 0 to n-1
根据神奇的欧拉公式 —— e ^( i θ) = cosθ + i sinθ
e^2 π i = 1
wn =e^2 π i / n So wn^ n = 1
wn^n ^ k=1 So e^(2πki/n) =1
这 n 个 单位复数根 各不相同
且 在乘法意义下 构成一个 群
FFT 之 Cooley-Tukey (蝶形算法)
A[0]( x ) = a0 + a2 x + a4 x^2 + ... + a(n-2) x^(n/2-1)
A[1]( x ) = a1 + a3 x + a5 x^3 + ... + a(n-1) x^(n/2-1)
A(x)=A[0] ( x^2 ) + x A[1] ( x^2 )
消去引理…… (w[dn]) ^ dk = (w[n]) ^ k
折半引理…… (w[n])^k^2=(w[n/2])^k
求 A(x) 在 w[n]^0 , w[n]^1 ,w[n]^2, ... w[n]^(n-1) 的值
即 求
A[0](x^2) + x A[1](x^2)
A[0](x^2)
即
A[0](x) 在 w[n/2]^0 , w[n/2]^1 , w[n/2]^2 , ... w[n/2]^ (n/2-1) 的值
即A[0](x) 在 w[n]^(0 , 2 , 4 to n) 的值
递推,即 在 w[n]^n 的值
A( w[n]^k ) = A[0]( w[n]^ 2k) + w]n]^k * A[1]( w[n] ^ 2k)
= y[0]k + w[n]^k y[1]k
A( w[n]^(k+n/2 ) = A[0]( w[n]^ 2k+n) + w[n]^(k+n/2) * A[1]( w[n] ^ (2k+n) )
= A[0]( w[n] ^2k ) - w[n]^k * A[1]( w[n] ^ 2k)
= y[0]k - w[n]^k y[1]k
T(n) = 2T(n/2) + O(n) = O (nlogn)
引用自 https://www.zybuluo.com/TaoSama/note/171617 的伪代码
function RECURSIVE-FFT(a) n = a.length if n == 1 return a wn = e^(2*pi*i/n) w = 1 a0 = (a_0,a_2,...,a_n-2) a1 = (a_1,a_3,...,a_n-1) y0 = RECURSIVE-FFT(a0) y1 = RECURSIVE-FFT(a1) for k = 0 to n/2-1 y[k] = y0[k] + w * y1[k] y[k+n/2] = y0[k] - w * y1[k] w = w * wn
NTT
由于FFT中会出现很多设计正余弦的值,所以精度不能尽如人意
所以NTT 问世 其精度更高
太懒待续……
学习自以下文章
https://www.zybuluo.com/TaoSama/note/171617 FFT
http://blog.csdn.net/liangzhaoyang1/article/details/52769839 FFT
http://blog.csdn.net/zz_1215/article/details/40430041 NTT