模板:快速傅里葉變換(FFT)

參考: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)=i=0n1aixi,B(x)=i=0n1bixi ,求A(x)×B(x) 後的多項式係數。

初探:

顯然我們有一個O(n2) 的解法,但是實在是太慢了。
考慮到一個n1 次多項式可以看做是定義在複數域上的函數,則我們一定可以找到n個點來唯一確定這個函數。
當然我們也可以通過這些點來表示這個多項式。
假設:
A(x) 被表示爲:<(x0,ya0),(x1,ya1),,(x2n2,ya2n2)>
B(x) 被表示爲:<(x0,yb0),(x1,yb1),,(x2n2,yb2n2)>
顯然A(x)×B(x) 被表示爲:<(x0,ya0yb0),(x1,ya1yb1),,(x2n2,ya2n2yb2n2)>

這裏多取了點的原因在於A(x)×B(x) 是一個2n2 次多項式,則至少要取2n1 個點才能保證正確。

但是顯然還是O(n2) 的。

再試:

考慮設A(xi)=A0(xi2)+xiA1(xi2) ,其中:
A0(x)=a0+a2x+a4x2++an2xn2+1
A1(x)=a1+a3x+a5x2++an1xn2+1

其實就是按照係數下標的奇偶性分類了一下。

此時我們再令取點的x 值爲<x0,x1,,xn21,x0,x1,,xn21>
我們發現把x 平方後我們的取值瞬間縮小了一半,而原式唯一變化的就是A1(x) 前的符號。
看起來我們似乎找到了O(nlogn) 的可行方案。
但是很可惜,這樣優秀的x 取值的性質只會保留一次,也就是說我們只是得到了一個O(n22)
如何才能每次將問題的規模縮小一半是我們的目標。

插曲:

有個人告訴你:不如試試Xn=cos2πn+i×sin2πn0n1 次方作爲x 的取值。

這塊大家一直有個疑惑:這是怎麼構造出來的啊?
事實上傅里葉變換最早是應用於信號處理上的,傅里葉提出:任何連續週期信號可以由一組適當的正弦曲線組合而成。
多項式可以看做非連續週期信號,然後通過各種奇妙的姿勢讓它逼近正弦曲線的組合形,詳情可以看鬆鬆鬆WC2018的課件。
“逼近”顯然用到了微積分,不適合初學者,所以就直接跳過了。(其實我也不會……)
(再多說一點吧,其實上面和下面的數學推理完全可以從物理層面理解,還是可以參考鬆鬆鬆WC2018的課件)

繼續:

那麼令取點的x 值爲<Xn0,Xn1,,Xnn1>
我們可知:
(Xnk)2

=Xn2k

=cos2k×2πn+i×sin2k×2πn

=cos2kπn2+i×sin2kπn2

=Xn2k


Xnk

=cosk×2πn+i×sink×2πn

根據三角函數的週期性可知,kn 取模顯然不會對答案造成影響。
於是我們有Xnk=Xnk%n

那麼顯然對於<(Xn0)2,(Xn1)2,,(Xnn1)2>

它等效於<Xn20,Xn21,,Xn2n21,Xn20,Xn21,,Xn2n21>

我們好像看到了O(nlogn) 的曙光了。

尾聲:

顯然我們可以對x 的取值折半,然後對於左右區間的x 值遞歸下去即可。
Q1:誒等等,“再試”裏面的內容好像沒有應用上啊……

A1:那就轉化一下,其實我們只需要求一個區間的A0(x)A1(x) 值遞歸下去求A(x) 即可。
也就是說其實我們是得到了:
<(A0)0,(A0)1,,(A0)n21,(A1)0,(A1)1,,(A1)n21>

Q2:這好像是畫蛇添足……

A2:emmm……我說這個可以用於常數優化你信嗎……
顯然A(Xnk)=(A0)k%n2+Xnk(A1)k%n2

取模是因爲,不要忘了我們的取值是由兩個一樣的左右區間合併在一起的。

那麼我們得到了<A0,A1,,An1>

(其中Ak=A(Xnk)

我們好像把這個序列的長度減少了一半誒!那自然是快了二倍啊。

不要忘了n要滿足始終是2的倍數,所以n要取2的整數次冪,同時將沒用的次冪的係數填成0。

Q3:IFFT怎麼做啊?

A3:繼續看下去……?

補遺:

略講一下IFFT。
顯然我們可以把FFT的最初算法(也就是DFT)看做兩個矩陣相乘。

兩個矩陣分別一個填(Xnk)m ,一個填係數,可以上參考處原博客看矩陣。

那麼我們把第一個矩陣變成逆矩陣豈不是爲IFFT?
其實就是這樣,並且事實上就是填((Xnk)m)/n ,具體證明過程看參考處原博客。
剩下的做法就和FFT一樣啦。

謝幕:

(是的我沒有講實現,因爲我也是今天才學完)
(然而學完和代碼能寫出來是兩個概念,所以先咕咕咕了)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章