【从0开始ACM】【LV3】【数学】【 FFT 快速傅里叶变换 && NTT 更高精度的 快速数论变换】

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 ) +  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






你可能感兴趣的:(【从0开始ACM】【LV3】【数学】【 FFT 快速傅里叶变换 && NTT 更高精度的 快速数论变换】)