这几天在家没事慢慢研究了一下集训最后的一个专题FFT, 陈犇讲完还是有点不是很懂, 网上的很多资料都讲的不是很清晰, 由于这个算法太美了, 我有必要写点什么东西表示对前人智慧的赞美! (顺便练练打公式)
FFT ( fast Fourier transform )的中文名是快速傅里叶变换, 在信号领域有很大的作用, 在ACM中我们常用FFT来求解多项式乘法
y1=2x4+3x2+5x+7 y2=5x3+7x2+11 FFT就是用来快速地计算 y1⋅y2 所得到的多项式 ( y1⋅y2=10x7+14x6+15x5+68x4+70x3+82x2+55x+77 )
A(x)=a0+a1x+a2x2+⋯+an−2xn−2+an−1xn−1
A(x)=∑i=0n−1aixif(x)=2+3x+x2 这个多项式的系数表达式就是 (2,3,1) , 点值表达式就是 {(0,2),(1,6),(2,12)} 或者 {(1,6),(2,12),(3,20),(4,30)} n−1 次多项式只要选取任意不同的 n 个点(或以上)都是可以的
显然不管是点值表达式还是系数表达式都可以唯一确定一个n−1次多项式, 并且它们是可以相互推导的
已知 f1(x)=x2+1,f2(x)=x+3 f1(x) 的点值表达式是 {(0,1),(1,2),(2,5),(3,10)} f2(x) 的点值表达式是 {(0,3),(1,4),(2,5),(3,6)} 那么 f1(x)⋅f2(x) 的点值表达式就是 {(0,3),(1,8),(2,25),(3,60)}
先不考虑什么叫做单位复根 先在平面直角坐标系下画一个单位圆, 即 {(x,y)|x2+y2=1} 现在我们要在这个圆上取 n 个点(n>0),使得这 n 个点恰好把圆弧分成了n等份, 规定第 1 个点一定是(1,0)
当 n=3 时, 这些点的坐标按照逆时针顺序分别是
(1,0),(−12,3√2),(−12,−3√2) 当 n=4 时, 这些点的坐标按照 逆时针顺序分别是 (1,0),(0,1),(−1,0),(0,−1) 当 n=8 时, 这些点的坐标按照 逆时针顺序分别是 (1,0),(2√2,2√2),(0,1),(−2√2,2√2), (−1,0),(−2√2,−2√2),(0,1),(2√2,−2√2) 如果我们把 平面直角坐标系看做是 复平面, 那么上面那些 点就变成了一个 复数, 它们正是 单位复根方程 xn=1 在复数域内一定有 n 个不同的解 那么规定: wkn是方程 xn=1 的第 k+1 个根 (0≤k<n) 也就是对应了上面我们从圆上逆时针选取的 n 个点中的第k个点
w02=1 (x2=1 的第 1 个根是 1) w12=1 (x2=1 的第 2 个根是 −1)
w04=1 (x4=1 的第 1 个根是 1) w14=i (x4=1 的第 2 个根是 i) w24=1 (x4=1 的第 3 个根是 −1) w34=i (x4=1 的第 4 个根是 −i)
w08=1 (x8=1 的第 1 个根是 1) w18=i (x8=1 的第 2 个根是 2√2 2√2i) w28=1 (x8=1 的第 3 个根是 i) w38=i (x8=1 的第 4 个根是 −2√2 2√2i) w48=1 (x8=1 的第 5 个根是 −1) w58=i (x8=1 的第 6 个根是 −2√2−2√2i) w68=1 (x8=1 的第 7 个根是 −i) w78=i (x8=1 的第 8 个根是 2√2−2√2i)
我们不难归纳得到:
wkn=cos(2πkn)+sin(2πkn)i 规定: 当 k≥n 时, wkn=wk%nn 例如: w64=w24,w208=w48下面的只讨论 n 是2的次方时,即: n=2m,(m≥0) 时的性质 画图我们可以很容易的证明
(wkn)2=wkn/2 例如: (w38)2=w34 , (w68)2=w64=w24用一句话来说就是:将原多项式的系数表达式转换成点值表达式 (O(nlog2n)) , 再将多项式的点值表达式相乘得到目标表达式的点值表达式 (O(n)) , 再将点值表达式转换成为系数表达式 (O(nlog2n))
为了能够在 O(nlog2n) 时间内得到多项式的点值表达式, FFT采取了一些小技巧, 选择 n 个单位复根进行求值, 由于单位复根的一些性质, 我们可以在O(nlog2n)求出它们的值
现在我们有一个最高次幂是 m 的多项式: A(x)=a0 a1x a2x ⋯ am−1xm−1 amxm 1.先将这个多项式的最高次数扩充到 2 的次方n−1
A(x)=a0+a1x+a2x+⋯+amxm+0⋅xm+1+0⋅xm+2+⋯+0⋅xn−1(n−1≥m)=a0+a1x+a2x2+⋯+an−2xn−2+an−1xn−1 2.分别带入 n 个不同的单位复根(w0n,w1n,w2n,...,wn−1n) ⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪A(w0n)=a0+a1w0n+⋯+an−1(w0n)n−1A(w1n)=a0+a1w1n+⋯+an−1(w1n)n−1A(w2n)=a0+a1w2n+⋯+an−1(w2n)n−1⋯⋯A(wn−2n)=a0+a1wn−2n⋯+an−1(wn−2n)n−1A(wn−1n)=a0+a1wn−1n⋯+an−1(wn−1n)n−1 可以写成一个矩阵: ⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜A(w0n)A(w1n)A(w2n)⋮A(wn−2n)A(wn−1n)⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜111⋮11w0nw1nw2n⋮wn−2nwn−1n(w0n)2(w1n)2(w2n)2⋮(wn−2n)2(wn−1n)2⋯⋯⋯⋱⋯⋯(w0n)n−2(w1n)n−2(w2n)n−2⋮(wn−2n)n−2(wn−1n)n−2(w0n)n−1(w1n)n−1(w2n)n−1⋮(wn−2n)n−1(wn−1n)n−1⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜a0a1a2⋮an−2an−1⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟ 没错我们就是要在 O(nlog2n) 时间内得到 A(w0n),A(w1n),A(w2n),⋯,A(wn−2n),A(wn−1n) 这 n 个值 ! ! ! 3.单独考虑其中任意一个方程 A(wkn)=a0 a1wkn a2(wkn)2 ⋯ an−1(wkn)n−1 3.将奇数项和偶数项分开 得到 A(wkn)=(a0+a2(wkn)2+⋯+an−2(wkn)n−2)+(a1wkn+a3(wkn)3+⋯+an−1(wkn)n−1)=(a0+a2(wkn)2+⋯+an−2(wkn)n−2)+wkn(a1+a3(wkn)2+⋯+an−1(wkn)n−2)=(a0+a2wkn/2+⋯+an−2(wkn/2)n2−1)+wkn(a1+a3wkn/2+⋯+an−1(wkn/2)n2−1) 4.假设两个新的多项式: A0(x)=a0+a2x+⋯+an−2xn2−1 A1(x)=a1+a3x+⋯+an−1xn2−1 那么 A(wkn)=A0(wkn/2)+wknA1(wkn/2) 我们只要再次用同样的方法计算 A0(x) 以及 A1(x) 就可以在 O(nlog2n) 时间内求出 A(x) 的所有点值表达式了多项式 f(x)=a0+a1x+a2x2+a3x3 , 我们要计算 A(w04),A(w14),A(w24),A(w34) 这4个点的值 1. A(w04)=A0(w02)+w04A1(w02) A(w14)=A0(w12)+w14A1(w12) A(w24)=A0(w02)+w24A1(w02) A(w34)=A0(w12)+w34A1(w12) 2. A(w02)=A0(w01)+w02A1(w01) A(w12)=A0(w01)+w12A1(w01) 3. 因为 w01=1 所以可以直接得到它的值, 也就是当前的系数
我们将多个多项式的点值表达式相乘, 获得了目标表达式的点值表达式 那么: 怎么获得目标表达式的系数表达式呢?
⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜A(x0)A(x1)A(x2)⋮A(xn−2)A(xn−1)⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟=⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜111⋮11x0x1x2⋮xn−2xn−1(x0)2(x1)2(x2)2⋮(xn−2)2(xn−1)2⋯⋯⋯⋱⋯⋯(x0)n−2(x1)n−2(x2)n−2⋮(xn−2)n−2(xn−1)n−2(x0)n−1(x1)n−1(x2)n−1⋮(xn−2)n−1(xn−1)n−1⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜a0a1a2⋮an−2an−1⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟
⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜a0a1a2⋮an−2an−1⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟=1n⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜111⋮11x−10x−11x−12⋮x−1n−2x−1n−1(x0)−2(x1)−2(x2)−2⋮(xn−2)−2(xn−1)−2⋯⋯⋯⋱⋯⋯(x0)−n+2(x1)−n+2(x2)−n+2⋮(xn−2)−n+2(xn−1)−n+2(x0)−n+1(x1)−n+1(x2)−n+1⋮(xn−2)−n+1(xn−1)−n+1⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎛⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜A(x0)A(x1)A(x2)⋮A(xn−2)A(xn−1)⎞⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟ 其实差异并不大, 我们只需要将原来DFT中 wkn 换成 w−kn ,结果再乘上 1n 就从 DFT 变成了 IDFT