參考:http://blog.csdn.net/f_zyj/article/details/76037583
原文即是一篇很好的FFT入門博客,但是筆者打算爲了日後的學習,則將原篇章的結構刪改增添一下,如有思路上的雷同十分正常。
“是時候打開FFT的大門了!”
預備知識:
1.至少知道基礎數論與一定解三角形知識(大概是高中水平)。
2.定義i=−1−−−√
3.引入複數(即形如a+bi (a,b均爲實數)的數的集合)
4.(cosθ+i×sinθ)k=cos(kθ)+i×sin(kθ)
5.顯然我們對多項式FFT之後得到的答案不是我們想要的,那麼這時候就需要反着用FFT把式子再變回去(本文記做IFFT)。
這裏證明一下第四條,用歸納法。
顯然當k=1 時成立。
當k 成立時,我們有:
(cosθ+i×sinθ)k+1
=(cosθ+i×sinθ)k×(cosθ+i×sinθ)
=(cos(kθ)+i×sin(kθ))×(cosθ+i×sinθ)
=cos(kθ)cosθ+i×sin(kθ)cosθ+i×cos(kθ)sinθ+i2×sin(kθ)sinθ
=cos(kθ)cosθ−sin(kθ)sinθ+i×(sin(kθ)cosθ+cos(kθ)sinθ)
=cos((k+1)θ)+i×sin((k+1)θ)
得證。
問題引入:
設A(x)=∑n−1i=0aixi,B(x)=∑n−1i=0bixi ,求A(x)×B(x) 後的多項式係數。
初探:
顯然我們有一個O(n2) 的解法,但是實在是太慢了。
考慮到一個n−1 次多項式可以看做是定義在複數域上的函數,則我們一定可以找到n個點來唯一確定這個函數。
當然我們也可以通過這些點來表示這個多項式。
假設:
A(x) 被表示爲:<(x0,ya0),(x1,ya1),…,(x2n−2,ya2n−2)>
B(x) 被表示爲:<(x0,yb0),(x1,yb1),…,(x2n−2,yb2n−2)>
顯然A(x)×B(x) 被表示爲:<(x0,ya0yb0),(x1,ya1yb1),…,(x2n−2,ya2n−2yb2n−2)>
這裏多取了點的原因在於A(x)×B(x) 是一個2n−2 次多項式,則至少要取2n−1 個點才能保證正確。
但是顯然還是O(n2) 的。
再試:
考慮設A(xi)=A0(x2i)+xiA1(x2i) ,其中:
A0(x)=a0+a2x+a4x2+…+an−2xn2+1
A1(x)=a1+a3x+a5x2+…+an−1xn2+1
其實就是按照係數下標的奇偶性分類了一下。
此時我們再令取點的x 值爲<x0,x1,…,xn2−1,−x0,−x1,…,−xn2−1>
我們發現把x 平方後我們的取值瞬間縮小了一半,而原式唯一變化的就是A1(x) 前的符號。
看起來我們似乎找到了O(nlogn) 的可行方案。
但是很可惜,這樣優秀的x 取值的性質只會保留一次,也就是說我們只是得到了一個O(n22) 。
如何才能每次將問題的規模縮小一半是我們的目標。
插曲:
有個人告訴你:不如試試Xn=cos2πn+i×sin2πn 的 0…n−1 次方作爲x 的取值。
這塊大家一直有個疑惑:這是怎麼構造出來的啊?
事實上傅里葉變換最早是應用於信號處理上的,傅里葉提出:任何連續週期信號可以由一組適當的正弦曲線組合而成。
多項式可以看做非連續週期信號,然後通過各種奇妙的姿勢讓它逼近正弦曲線的組合形,詳情可以看鬆鬆鬆WC2018的課件。
“逼近”顯然用到了微積分,不適合初學者,所以就直接跳過了。(其實我也不會……)
(再多說一點吧,其實上面和下面的數學推理完全可以從物理層面理解,還是可以參考鬆鬆鬆WC2018的課件)
繼續:
那麼令取點的x 值爲<X0n,X1n,…,Xn−1n>
我們可知:
(Xkn)2
=X2kn
=cos2k×2πn+i×sin2k×2πn
=cos2kπn2+i×sin2kπn2
=Xkn2
Xkn
=cosk×2πn+i×sink×2πn
根據三角函數的週期性可知,k 對n 取模顯然不會對答案造成影響。
於是我們有Xkn=Xk%nn
那麼顯然對於<(X0n)2,(X1n)2,…,(Xn−1n)2>
它等效於<X0n2,X1n2,…,Xn2−1n2,X0n2,X1n2,…,Xn2−1n2>
我們好像看到了O(nlogn) 的曙光了。
尾聲:
顯然我們可以對x 的取值折半,然後對於左右區間的x 值遞歸下去即可。
Q1:誒等等,“再試”裏面的內容好像沒有應用上啊……
A1:那就轉化一下,其實我們只需要求一個區間的A0(x) 和A1(x) 值遞歸下去求A(x) 即可。
也就是說其實我們是得到了:
<(A0)0,(A0)1,…,(A0)n2−1,(A1)0,(A1)1,…,(A1)n2−1>
Q2:這好像是畫蛇添足……
A2:emmm……我說這個可以用於常數優化你信嗎……
顯然A(Xkn)=(A0)k%n2+Xkn(A1)k%n2
取模是因爲,不要忘了我們的取值是由兩個一樣的左右區間合併在一起的。
那麼我們得到了<A0,A1,…,An−1>
(其中Ak=A(Xkn) )
我們好像把這個序列的長度減少了一半誒!那自然是快了二倍啊。
不要忘了n要滿足始終是2的倍數,所以n要取2的整數次冪,同時將沒用的次冪的係數填成0。
Q3:IFFT怎麼做啊?
A3:繼續看下去……?
補遺:
略講一下IFFT。
顯然我們可以把FFT的最初算法(也就是DFT)看做兩個矩陣相乘。
兩個矩陣分別一個填(Xkn)m ,一個填係數,可以上參考處原博客看矩陣。
那麼我們把第一個矩陣變成逆矩陣豈不是爲IFFT?
其實就是這樣,並且事實上就是填((X−kn)m)/n ,具體證明過程看參考處原博客。
剩下的做法就和FFT一樣啦。
謝幕:
(是的我沒有講實現,因爲我也是今天才學完)
(然而學完和代碼能寫出來是兩個概念,所以先咕咕咕了)