FFT學習筆記(初學者必備,超詳細)

前言:

下了好大決心,花了一個晚上的時間,終於看懂了FFT的理論與流程,然後又花了一個晚上實現代碼,做了幾道模板題。
FFT理論很深,卻又很實用,一個很常用的用法就是加速多項式乘法,使得原來O(n^2)的複雜度減小到O(nlogn)。
下面我來大概講述一下FFT的基本理論與算法流程,幫助初學者瞭解FFT,同時也是自己的複習。

<1> 介紹:

FFT,全稱快速傅里葉變換(fast Fourier transform),是用來計算離散傅里葉變換(DFT)及其逆變換(IDFT)的快速算法。對於DFT,迷一點講就是說它把時域信號轉化爲頻域信號(不明白也沒關係,並不影響學習算法)。在算法競賽中,我們經常用FFT來加速卷積,做一些多項式乘法或是高精度乘法之類的。

我主要基於多項式乘法來介紹FFT,下面也都是用多項式乘法來講的~


<2>一些定義:

我們將一個以x爲變量的多項式A(x)表示爲:

A(x)=j=0n1ajxj

若多項式A(x)的最高次的非零係數爲ak,則稱這是一個k次多項式,而它的次數界爲>k的任意一個整數(一般取k+1)。
若有兩個以x爲變量的n次多項式A(x)B(x),則它們的積可表示爲:
C(x)=j=02n2cjxj

其中
cj=k=0jakbjk

C(x)是一個次數界爲2n-1的多項式。
另外,下面還大量提到了複數,並用專用符號 i 來表示虛數單位,i2=1

<3>係數表達與點值表達:

對一個次數界爲n的多項式A(x)而言,其係數表達是一個由係數組成的向量a=(a0,a1,...,an1)
而它的點值表達是一個由n個點值對組成的集合

(x0,y0),(x1,y1),...,(xn1,yn1)

使得對k=0,1,...,n1,所有的xk各不相同,
yk=A(xk)

對於點值表達,我們可以看成是把多項式A(x)當做一個以x爲自變量的函數,並把y作爲因變量,從其圖像中取n個點,取其座標,就得到點值表示。
我們可以看到,求出這個多項式的n個點值複雜度爲O(n2),後面可以看到,如果我們巧妙地選取點xk,就可加速這一過程,使複雜度降至O(nlogn)
我們將求值運算的逆稱爲插值(就是通過點值表達來求出係數表達),有一個定理(差值多項式的唯一性):對於任意n個點值對組成的集合,其中所有的xk都不同,那麼存在唯一的多項式A(x),滿足yk=A(xk),k=0,1,...,n1證明不再贅述,我們大概可以從兩點確定一條直線,三點確定一條拋物線來大概認定這個定理吧。
對於多項式乘法,點值表達是十分方便的。若C(x)=A(x)B(x),則對於任意xkC(xk)=A(xk)B(xk),即:
A(x)(x,ya)B(x)(x,yb), 則C(x)(x,yayb)

<4>FFT的大致流程:

現在我們已經能在線性時間內將兩個多項式進行乘法操作了,那麼剩下的問題就是如何快速實現多項式係數表達與點值表達的快速轉化。
上面提到如果巧妙地選取xk,就可以加速這一過程,而我們要選取的點,就是“單位複數根”,具體介紹將在下節展開。
現在給出FFT算法的大致流程:
*1.加倍次數界:將兩個多項式A(x)B(x)先補爲2n次(高次係數爲0);若不足2的整數次冪位,則再補至2的整數次冪位(以便二分分治處理,後面講)。
*2.求值:將係數表達式轉化爲點值表達式(用2n階的FFT(DFT),求出多項式在2n次單位復根處的值)。
*3.逐點相乘:將兩個多項式點值表達中的y逐個相乘,得道積的點值表達。
*4.差值:利用積的點值表達再用FFT(IDFT)求出係數表達。

如此,多項式乘法就做完了,我們從兩個多項式的係數表達,得到了積的係數表達。


<5>單位複數根:

學FFT,這是極其重要的一點,希望能完全理解其中的變換與結論,最好能手動推算一下,並不難推,大部分是基礎的指數運算,但推過與沒推過是有很大區別的。

n次單位復根是滿足wn=1的複數w。n次單位復根恰好有n個,爲e2πik/n,k=0,1,…n-1。
複數的指數形式的定義:

eiu=cos(u)+isin(u)

並且值
wn=e2πi/n

稱爲主n次單位根,所有其它n次單位復根都是wn的冪次。
可以得到wnn=w0n=1,意味着wkn=wkmodnn,(w1n=wn1n)。
消去引理:對任何整數 n>=0,k>=0,以及d>0,
wdkdn=wkn

推論:對任意偶數,有
wn/2n=w2=1

折半引理:如果n>0爲偶數,那麼n個n次單位複數根的平方的集合就是n/2個n/2次單位複數根的集合。

證明:
根據消去引理,有(wkn)2=wkn/2=(wk+n/2n)2。(wn/2n=1

求和引理:對任意整數n>=1和不能被n整除的非負整數k,有

j=0n1(wkn)j=0

注:用等差數列的求和公式推導即可。

<6>DFT:

我們希望計算次數界爲n的多項式A(x)=n1j=0ajxjw0nw1nw2n...wn1n處的值。對k=0,1,…,n-1,定義結果yk:

yk=A(wkn)=j=0n1ajwkjn

我們也記y=DFTn(a)

<7>FFT:

通過FFT,利用單位複數根的特殊性質,我們就可以在O(nlogn)的時間內求出DFTn(a),此時n恰好是2的整數次冪。
FFT採用分治,將原多項式偶數下標與奇數下標的係數分開,得到兩個新的n/2次多項式A[0](x)A[1](x):

A[0](x)=a0+a2x+a4x2+...+an2xn/21

A[1](x)=a1+a3x+a5x2+...+an1xn/21

其中,A[0](x)A[1](x)
於是有:
A(x)=A[0](x2)+xA[1](x2)


於是,就可以遞歸分治處理了。
下面給出FFT的僞代碼:
void  FFT(a){
      n=a.length
      if(n==1)  return a
      wn=e2πi/n
      w=1
      a[0]=(a0a2...an2)
      a[1]=(a1a3...an1)
      y[0]=FFT(a[0])
      y[1]=FFT(a[1])
      for k=0  to  n/21 
            yk=y[0]k+wy[1]k
            yk+(n/2)=y[0]kwy[1]k
            w=wwn
      return  y
}

注意:yky[0]ky[1]k求點值所用的單位復根是不同的,前者是wkn,而後者是wkn/2。後者等於前者的平方,對應前面的"x2"。於是,for循環中的第一個式子就很容易理解了,對於第二個式子,可以自己推導一下(提示:要用到1:wk+(n/2)n=wkn,2:w2k+nn=w2kn)。


<8>插值:

現在我們已經知道了如何快速的通過係數表達,在單位復根處快速求出點值,那麼,接下來就只剩下最後一項任務,那就是在快速時間內完成DFT的逆過程IDFT,在單位復根處插值,得到結果的係數表達。

我們可以把DFT寫成矩陣乘積y=Vna,其中Vn是一個由wn適當冪次填充成的範德蒙德矩陣,矩陣第i行,第J列的元素爲wijn(i,j從0到n-1)。
定理:V1n(j,k)wkjn/n
至於證明,只需把矩陣乘法各點的式子寫出來,結合求和引理,就可以發現VnV1n主對角線上的元素都爲1,其餘則都是0(也就是單位矩陣I)。
於是可以推出DFT1n(y):

aj=1/nk=0n1ykwkjn

其中j=01...n1
我們比較第6節中的式子,發現只要做出以下修改
*1:把a與y互換
*2:用w1n替換wn
*3:將計算結果每個除以n

最後再來一個定理
卷積定理:對任意兩個長度爲n的向量a和b,其中n是2的冪,

ab=DFT12n(DFT2n(a)DFT2n(b))

最後,感謝大家的閱讀!
如有不足之處,儘可在評論中指出。

本文參考文獻:
《算法競賽入門經典訓練指南》——劉汝佳
《算法導論》
(注:本文大部分內容均摘自算法導論,自己加以篩選、註釋,第7節中提到的推導過程書中有介紹,若沒想出來可以查閱(P534))

轉載自:http://blog.csdn.net/Monkey_king2017cn/article/details/77542160

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