快速傅里葉變換

FFT,即爲快速傅氏變換,是離散傅氏變換的快速算法,它是根據離散傅氏變換的奇、偶、虛、實等特性,對離散傅立葉變換的算法進行改進獲得的。它對傅氏變換的理論並沒有新的發現,但是對於在計算機系統或者說數字系統中應用離散傅立葉變換,可以說是進了一大步。這是360百科中對於fft的一種概念解釋。我們可以這麼理解:FFT(Fast Fourier Transformation)就是“快速傅里葉變換”的意思,它是一種用來計算DFT(離散傅里葉變換)和IDFT(離散傅里葉反變換)的一種快速算法。這種算法運用了一種高深的數學方式、把原來複雜度爲O(n2)的樸素多項式乘法轉化爲了O(nlogn)的算法。

那麼這種快速傅里葉變換是如何實現的呢?由於離散信號是由很多點組成的,所以DFT的公式如公式1所示。而連續信號的傅里葉變換是在連續域中進行的運算,其積分運算公式如公式2所示。離散傅里葉變換將這個積分運算變爲了累加運算,即使如此,實際運算量也依然非常可觀。所以在實際的應用場景中,離散傅里葉變換的實用性較差。因此,FFT作爲新的計算方式被廣泛接受。其本質是將離散傅里葉變換通過它本身的奇、偶、虛、實的性質,對該公式進行推導改進。公式1中的k和t均爲時域中的輸入參數,X(k)以及F[f(t)]即爲相對與x以及t的傅里葉變換。

                                      X(k) =\sum_{n=0}^{N-1}x_ne^{-j2\pi nk/N }                                                                                    公式1    

                                     F(w)=F[f(t)]=\int_{-\infty }^{\infty }f(t)e^{-jwt}dt                                                                  公式2                                                                                                          

首先我們對於離散傅里葉變換的公式進行轉換推導。離散傅里葉變換實際可以表示爲:

                                     X(k)=x_1 e^{-j2 \pi k/N}+x_2 e^{-j2 \pi k 2/N}+x_3 e^{-j2 \pi k 3/N}+\cdots x_n e^{-j2 \pi k 2k/N}             公式3

我們改變一種表示方式,離散傅里葉變換也可以表示爲

                                     F(x)=A_0+A_1 x+A_2 x^2+A_3 x^3+\cdots +A_n x^n                                              公式4

對於這種公式,我們應該如何來進行求解呢。在這裏我們不進行傳統計算方式的介紹,直接按照fft的思想來進行解析推導。

首先,我們對於多項式進行分解,現在我們將該多項式分解爲奇數次項和偶數次項的進行分類表示。而且爲了表現的更清楚,我們先將該多項式的最高次項定爲7,即有8個多項式進行累加,然後重新排列後的多項式爲:

                                     F(x)=(A_0+A_2 x^2+A_4 x^4+A_6 x^6)+x(A_1+A_3 x^2+A_5 x^4+A_7 x^7)         公式5

爲了表示的更清晰,我們分別給奇數次項和偶數次項重新起一個名字,即f(x)和g(x),這兩個多項式依次表示爲:

                                     f(x)=A_0+A_2 x^2+A_4 x^4+A_6 x^6                                                                    公式6

                                     g(x)=A_1+A_3 x^2+A_5 x^4+A_7 x^6                                                                    公式7

對該公式進行還原,我們又可以還原回原來的總公式。

                                     F(x)=f(x^2 )+xg(x^2)                                                                                      公式8

之前我們的思維一直停留在實數範疇內,現在我們引入一個複數域內的概念。我們叫它復根用ω 表示。如果w^k=1 ,那麼我們稱“ω 爲1的k次復根”計做 w_{k}^{n} ,單位復根地表示,其中n就是一個序號數,我們把所有的負根按照複數域角度的大小逆時針排序從零開始編號。

如下圖所示,即爲一個四次復根的圖形表示。

                                                                                         圖 1 四次復根

根據該圖我們可以推論,其實k次復根就相當於是將圖中的圓周平均分成k個弧,弧與弧之間的端點就是k次復根。另外,從圖中可以看出 w_{4}^{2} = -1 = i^2 = (w_{4}^{1})^2w_{4}^{0} 是這個圓與“Real”軸即實數軸正半軸的交點,所以無論k取多少,w_{k}^{0}  始終是1。我們只需要知道 w_{k}^{1},就能求出 w_{k}^{n},所以我們稱 w_{k}^{1} 爲“單位復根”。

在正常使用過程中,我們也能夠w_{k}表示單位復根,w_{k}^{1} 表示的是“單位復根”的“1次方”也就是它本身,其他的就叫做k次單位復根的n次方。

關於單位復根,它還有一些特性,

  1. n次單位復根的值隨指數變化而循環,w_{k}^{n+k} = w_{k}^{k}w_{k}^{n} = w_{k}^{n} 。
  2. 折半引理: w_{k}^{n} = w_{k/2}^{n/2},這個引理在fft的公式推導優化過程中會使用到,如圖1中,如果將整個圓周劃分爲兩個2個弧,則w_{2}^{1}的位置實際就是現在w_{4}^{2} 的位置,因此該引理成立。
  3. 消去定理:w_{k}^{n+k/2} = -w_{k}^{n},這個定理可以由上圖中看出,復根轉了半圈正好變成了-1。

現在我們將單位復根帶入上述的公式中代替x的值,則原來的公式可以表示爲:

                                     F(w_{k}^{n}) = f(w_{k}^{2n})+w_{k}g(w_{k}^{2n})                                                                              公式9

由於需要對n進行分類討論,因爲涉及到奇偶次項的運算,當0 <n <k/2 -1時 ,此時根據之前提到的復根的折半引理,我們可以將該公式推導爲:

                                     F(w_{k}^{n}) = f(w_{k/2}^{n})+w_{k}g(w_{k/2}^{n})                                                                           公式10

當k/2<n +k/2<k -1時,上述的公式又可以變爲

                                     F(w_{k}^{n+k/2}) = f(w_{k}^{2n+k})+w_{k}^{n+k/2}g(w_{k}^{2n+k})                                                        公式11

根據之前的消去定理以及折半定理,我們可以將公式優化爲:

                                     F(w_{k}^{n+k/2}) = f(w_{k/2}^{n})-(w_{k/2}^{n})g(w_{k/2}^{n})                                                              公式12

所以,fft的計算公式可以總結爲兩個區域內的值表示,即0 <n <k/2 -1以及k/2<n +k/2<k -1的區間範圍內所有Fx 的值都可以表示,另外,可以看出fx 以及 gx 就是 Fx 的一半,因此在程序計算過程中可以按照子問題的方式來遞歸求解。

以下即爲根據上述推導處的最後兩個公式來進行計算的fft代碼表示。代碼中涉及到的子函數均爲複數間的計算。

void fft(complex *a,int n,int dft)//n表示我的多項位數
{
	int  i = 0, j = 0, k = 0;
	int step = 0;
	for(i=0;i<n;i++) if(i<rev[i]) Swap(a[i],a[rev[i]]);
	for(step=1;step<n;step<<=1)//模擬一個合併的過程
	{
		complex wn;
		Wn_i(n,1,&wn,dft);//計算當前單位復根
		for(j=0;j<n;j+=step<<1)
		{
			complex wnk;
			Wn_i(n,1,&wnk,dft);//計算當前單位復根
			for(k=j;k<j+step;k++)
			{//蝴蝶操作
				complex x=a[k];
				complex y;
				c_mul(a[k+step],wnk,&y);
				c_plus(x,y,&a[k]);
				c_sub(x,y,&a[k+step]);//後半個“step”中的ω一定和“前半個”中的成相反數
				c_mul(wnk,wn,&wnk);
			}
		}
	}
}

爲了得到蝶形計算的參數序號,在進行fft計算之前先要進行參數重新排序,該函數的目的就是將每一位顛倒。如果暴力按位反轉,總歸不夠優雅。所以,我們用一個類似DP動態規劃的方法來實現這個功能。代碼如下所示:

void get_rev(int bit)//bit表示二進制的位數
{  
    for(int i=0;i<(1<<bit);i++)//我麼要對1~2^bit-1中的所有數做長度爲bit的二進制翻轉
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));//?!! SMG ?!!
}

根據dp的思想,每一個問題都可以由其子問題的解來進行求解。所以我們可以把一個二進制數看成兩部分,它的前bit-1位是一部分,它的最後一位是一部分。全部bit位的數據求解看成是總問題,則前bit-1位可以看成是一個子問題。所以,要求解所有位數據的反轉,我們可以直接利用前bit-1位數據的反轉結果。因此,任意一個數的二進制反轉就相當於是把它的最後一位當成首位,然後在後面接上它前bit-1位的二進制反轉。而且在這個循環中我們能保證,在計算i的二進制反轉之前,1 ~ i-1中的所有數的二進制反轉都已經完成。 i的前bit-1位的數值其實就是i >>1的值,直接調用 i >>1的二進制反轉的結果就相當於調用了i 的前bit-1位二進制反轉的結果。

其實i >>1的反轉與i的前bit-1位的反轉是有一點出入的,因爲我們的二進制反轉始終以bit位爲標準,所以i >>1會比i的前bit-1位多出一個前導零,而反轉之後就會多出一個後綴零,所以i的前bit-1位的反轉要去掉那個後綴零,此處我們通過將結果向右移位來實現,也就是rev[i>>1]>>1。

因此,我們只要把末尾乘上2^(bit-1)變成首位,此時該數據除了最高位有效之外,其餘所有數位全都是向左移位產生的0,所以,再按位或(|)上rev[ i >>1]>>1就是我們要的答案了。

關於快速傅里葉變換,我的學習過程屬於比較坎坷的一類,大學期間曾經學習過一段時間,但那時對於這些概念沒有深入的學習,所以壓根沒有理解。即使後來再次遇到這些概念及公式也是一頭霧水。現在由於工作過程中需要用到這些知識,所以便重新回來翻看。快速傅里葉變換作爲工程上的一種工具,通過很巧妙的手段將公式重組簡化,大大提高了多項式乘法以及離散傅里葉變換的計算速度。學習期間上網查閱過很多前輩總結的學習筆記,在此向各位提供學習心得的前輩表示衷心的感謝!

 

 

參考文獻

https://blog.csdn.net/WADuan2/article/details/79529900

https://blog.csdn.net/tf18269639242/article/details/53024276

https://blog.csdn.net/f_zyj/article/details/76037583

https://wenku.baidu.com/view/5cacb2b8bd64783e09122b9a.html

https://blog.csdn.net/chenyujing1234/article/details/7419863

https://www.cnblogs.com/luoqingyu/p/5930181.html

https://blog.csdn.net/ggn_2015/article/details/68922404

https://blog.csdn.net/egean/article/details/53039248

https://blog.csdn.net/ggn_2015/article/details/68922404

https://blog.csdn.net/linwanglian1/article/details/56020221

https://www.cnblogs.com/RabbitHu/p/FFT.html

https://www.cnblogs.com/Lyush/articles/3219196.html

https://blog.csdn.net/zhaopeizhaopeipei/article/details/53908238

https://blog.csdn.net/shenziheng1/article/details/52891807

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