1. 連續傅立葉變換(Continuous Fourier Transform)
對於時域連續函數 ,它的傅立葉正變換(FT)定義為
(用角頻率
表示) 或者
(用頻率
表示,
)
傅立葉逆變換(inverse FT)定義為
2. 離散傅立葉變換(Discrete Fourier Transform, DFT)
如果有等時間間距 的時域信號N個:
,它的離散傅立葉變換(DFT)定義為
這里 . 相應的逆變換定義為
3. 快速傅立葉變換(Fast Fourier Transform, FFT)
快速傅立葉變換FFT是一種更加高效的計算離散傅立葉變換的算法,有着 的計算復雜度,比原始的DFT
計算復雜度有更好的可擴展性。Cooley-Tuckey算法是一種常見的FFT實現方式,但是需要限制輸入數據點數
(2的乘方)形式。
如果你的數據數量不是2的幾次冪,可以通過在后面加0(zero padding)的方式湊成下一個2的乘方個數據,添加的0不會影響頻域譜,只會增加表觀數據密度,即多出來的頻域點其實是有效數據的線性內插值 Linear interpolation,如下圖所示:
對於zero padding證明如下:
如果記原始時域信號的離散傅立葉變換為 ,其中時域數據數為
. 我們添加0到后面,記zero padding的信號為
, N 是2的乘方。所以
的離散傅立葉變換為
離散傅立葉變換得到的頻域信號是以 為間距的,最大頻率稱做Nyquist頻率,即為
,它只跟時域信號的步長有關。經過FFT變換的頻域信號對應的下列頻率
也就是說,前一半是正的頻率,后一半是負的頻率,中間點是正負最大頻率。一般畫圖的話,需要把后一半挪到前面。
連續傅里葉變換













如果寫成一般形式,用 替換原信號,用
替換40,得到
問題來了,雖然貌似聯系很緊密,但這怎么跟DFT的公式長得不一樣。。。DFT的公式應該是這樣的:
就算用歐拉公式展開,我們得到的是
鏈接:https://www.zhihu.com/question/21314374/answer/542909849
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
覺得現有答案都是從連續傅里葉變換引出離散的,各位大神功底很扎實啊。但問題是很多同學就是因為對那些繁瑣的 和
感到暈厥,才轉到沒有積分,只有
,看着比較簡單的DFT來試試。所以DFT這樣明顯更簡單的一個東西為什么一定要從連續傅里葉變換出發來講呢?換個思路不行嗎?
本文直接從DFT出發,不需要連續傅里葉變換的知識。
DFT做了什么事?
對一段有限長的離散信號,找出它含有的各個頻率的正弦波分量。(這是句大家都知道的廢話)
但如果換個單獨針對DFT的解釋就會好理解很多:
在長度為N的離散信號中,針對k=(0,1,2...),分別找出在長度N內振動k個周期的三角波分量的權值
怎么做呢?舉個例子,大家可以打開MATLAB一起做
拋開時間拋開采樣頻率,只看點的個數,我們對某個余弦信號在兩個周期內采樣了40次:
n = 0:39; y = cos(2*pi*(2*n)/40); stem(n,y);

現在我們想通過DFT知道它在40次采樣時間內振動了幾個周期,算法是怎么處理的呢?其實很暴力:
事先選取40個長度為40個點的基信號,它們分別長這樣:
第一個,40次采樣內振動0個周期: ,即常值

第二個,40次采樣內振動1個周期:

第三個,40次采樣內振動2個周期:

第四個,40次采樣內振動3個周期:

第五個,40次采樣內振動4個周期:

……
以此類推一直到40個采樣內振動39個周期。
接下來,對於上述40個基信號,判斷它們跟原信號的相關程度。代數里判斷兩個長度相同的離散函數x[k],y[k]的相關程度,是用把它們在同一點的函數值相乘,再對得到的積求和的方法(就是向量求內積的方法),即下面的公式:
這個值越大,x[k]和y[k]長得越像。於是DFT把
到
這40個基函數都跟當前函數
比較了一下,發現
和
跟它長得最像!
這很顯然,因為
於是
下面,如果我們把這40次每次比較的correlation值記下來,就得到了原信號在每個頻率上的分量大小,比如,我要看它跟40次采樣中振動8次的那個基信號相似程度,就是:

把這40個correlation值記下來,就得到了原信號的頻域X:
stem(n, abs(fft(y)));

可以看到 ,其他值都是0
如果寫成一般形式,用 替換原信號,用
替換40,得到
問題來了,雖然貌似聯系很緊密,但這怎么跟DFT的公式長得不一樣。。。DFT的公式應該是這樣的:
就算用歐拉公式展開,我們得到的是
這又是為什么?
這是因為,對於一個信號,如果只跟余弦函數比較,會損失一些信息,比如相位。
如果我們的原信號有一些相位偏移,
如果對這個函數同樣按照上面的方法計算頻域,結果會有一些不一樣:
n = 0:39; x = cos(2 * pi * (2 * n)/40 + pi/4); stem(n, real(fft(x)));

雖然對應頻率還是篩選出來了,但是權值大小不一樣了,如果計算一下得知
在相位偏移前,這個值是20,是原來的 倍
注意如果我們再找一個信號 ,沒有相位偏移,但是直接把幅值砍到
,即:
那么這個信號的
跟 信號的結果一模一樣,這樣我們就無法通過頻域恢復信號了,或者說我們損失了信息
解決方法是另選一組以正弦函數(實際上選了負正弦)為基准的“基信號”,即 到
,計算另一組原信號與正弦基的相關系數,這兩組系數一起作為DFT的最終結果。而復數只是一個工具,用來方便地同時存儲兩組計算結果。當然它還有一個好處就是能夠比較直觀地表現出模和相位。
選負正弦還是正弦做基信號其實無所謂,只是最后的結果算出來的相位反一下而已,幅值是一樣的。如果一個信號跟某頻率余弦和負正弦的相關系數分別為a,b,那么代表這個信號差不多型如
根據高中數學可以求得其模為 ,(相對余弦的)相位為
,這與復數
的模和相位是相同的,因此DFT的公式
相當於同時把x[n]做了跟N個余弦基、N個負正弦基的比對,結果用N個復數存儲。如果想要看某個頻率的相位和模,就看對應復數的相位和模。
我們再來看看上面有相位偏移的那個例子:
原信號:
與余弦比對:
與負正弦比對:
在40個點內振動兩個周期這個頻率上,其DFT的結果為
其模為20,與相位偏移前相同。
其相位為 ,也沒有問題。
如果將原信號變為 ,會求得該頻率DFT結果為
,求得其相位為
。因此,根據DFT結果求得的相位是相對余弦信號的相位。
對於實際信號
上面做的分析沒有考慮時間和采樣頻率,但只要給定一個采樣頻率,時域的橫軸就可以換算成時間,頻域的橫軸就可以換算成頻率。
DFT所做的只是選取了在給定長度內振動了整數個周期的正弦和余弦波作為基。
上面的信號是40個采樣,如果給一個采樣頻率是100Hz,那么信號長度就是0.4s,原信號在40個采樣內振動了兩個周期,可以算出其頻率為5Hz
對於頻域,每個“在40個采樣內振動了k個周期”的基信號的實際頻率為 ,也就是說頻域圖中的每一個點代表2.5Hz,這個系統的頻域分辨率是2.5Hz,所以我們從
得知原信號的實際頻率為
FFT(Fast Fourier Transform)是什么
FFT是 Cooley & Tuket 兩人1965年提出的快速計算DFT的算法。這背后還有個故事,美國和蘇聯1963年簽了個核試驗禁令,互相約定大家都不搞核試驗了。但是美國不放心啊,怕毛子說一套做一套,肯尼迪就請了一堆科學家開會,說想搞一套不用去蘇聯檢查就能探測到核試驗的設備。美國在蘇聯周圍放了一圈聲波探測儀,但是遇到個問題——DFT算得太慢了。正巧 Tuket 和IBM的一個叫 Richard Garwin 的出席了那次會議。會上Garwin就想起來Tuket和Cooley好像合作過一個類似的算法,就讓Tuket去找Cooley,但是隱藏了真實目的,告訴Cooley這個算法是為了探測氦3晶體的自旋周期……(也是醉了orz)。兩人的算法最后於1965年發表,極大提升了DFT的計算速度,甚至被后世譽為“20世紀最偉大的工程算法”。
從DFT的公式可以看出其算法復雜度是 的,兩人的FFT算法表明,在N為合數的情況下
,原長度為N的DFT可以分解為兩個長度分別為
和
的DFT!
當然算法不停止於此,如果 和
還可以繼續分解,就可以分解為更多小DFT來計算。
使用了算法中的分治(divide and conquer)策略。
而由於DFT的長度其實是可以任意選擇的,因此通常在使用FFT時,都選擇2的整數次冪作為FFT長度,這樣可以一直分解到N/2個長度為2的DFT,復雜度直接降到
更詳細的解答,請移步我做的視頻:https://www.bilibili.com/video/av44600709
想從理論層面更深入了解DFT的,可以看我另外一個視頻:https://www.bilibili.com/video/BV1a
0x00 寫在前面
為了讓更多人能夠看到這個教程,希望大家收藏之前,也要點贊哦!!!蟹蟹大家的認可和鼓勵。
傅里葉變換
快速傅里葉變換(Fast Fourier Transform,FFT)是一種可在 時間內完成的離散傅里葉變換(Discrete Fourier transform,DFT)算法。
在算法競賽中的運用主要是用來加速多項式的乘法。
考慮到兩個多項式 的乘積
,假設
的項數為
,其系數構成的
維向量為
,
的項數為
,其系數構成的
維向量為
。
我們要求 的系數構成的
維的向量,先考慮朴素做法。
可以用這段代碼表示:
for ( int i = 0 ; i < n ; ++ i ) for ( int j = 0 ; j < m ; ++ j ) { c [i + j] += a [i] * b [j] ; }
思路非常清晰,其時間復雜度是 的。
所以我們來學習快速傅里葉變換。
0x01 關於多項式
多項式有兩種表示方法,系數表達法與點值表達法
多項式的系數表示法
設多項式 為一個
次的多項式,顯然,所有項的系數組成的系數向量
唯一確定了這個多項式。
多項式的點值表示法
將一組互不相同的 (叫插值節點)分別帶入
,得到
個取值
.
其中
定理:
一個次多項式在
個不同點的取值唯一確定了該多項式。
證明:
假設命題不成立,存在兩個不同的次多項式
,滿足對於任何
,有
。
令,則
也是一個
次多項式。對於任何
,都有
。
即有
個根,這與代數基本定理(一個
次多項式在復數域上有且僅有
個根)相矛盾,故
並不是一個
次多項式,推到矛盾。
原命題成立,證畢。
如果我們按照定義求一個多項式的點值表示,時間復雜度為
已知多項式的點值表示,求其系數表示,可以使用插值。朴素的插值算法時間復雜度為 。
關於多項式的乘法
已知在一組插值節點 中
(假設個多項式的項數相同,沒有的視為
)的點值向量分別為
,那么
的點值表達式可以在
的時間內求出,為
。
因為 的項數為
的項數之和。
設 分別有
項所以我們帶入的插值節點有至少有
個。
如果我們能快速通過點值表式求出系數表示,那么就搭起了它們之間的一座橋了。
這也是快速傅里葉變換的基本思路,由系數表達式到點值表達式到結果的點值表達式再到結果的系數表達式。
0x02 關於復數的基本了解
我們把形如 這樣的數叫做復數,復數集合用
來表示。其中
稱為實部
,
稱為虛部
,
為虛數單位,指滿足
的一個解
;此外,對於這樣對復數開偶次冪的數叫做虛數
.
每一個復數 都對應了一個平面上的向量
我們把這樣的平面稱為復平面
,它是由水平的實軸與垂直的虛軸建立起來的復數的幾何表示。
故每一個復數唯一對應了一個復平面上的向量,每一個復平面上的向量也唯一對應了一個復數。其中 既被認為是實數,也被認為是虛數。
其中復數 的模長
定義為
在復平面的距離到原點的距離,
。幅角
為實軸的正半軸正方向(逆時針)旋轉到
的有向角度。
由於虛數無法比較大小。復數之間的大小關系只存在等於與不等於兩種關系,兩個復數相等當且僅當實部虛部對應相等。對於虛部為 的復數之間是可以比較大小的,相當於實數之間的比較。
復數之間的運算滿足結合律,交換律和分配律。
由此定義復數之間的運算法則:
復數運算的加法滿足平行四邊形法則,乘法滿足幅角相加,模長相乘。
對於一個復數 ,它的共軛復數是
,
稱為
的復共軛
.
共軛復數有一些性質
0x03 復數中的單位根
復平面中的單位圓
其中 單位根,表示為
,可知
(順便一提著名的歐拉幅角公式 其實是由定義來的...)
將單位圓等分成 個部分(以單位圓與實軸正半軸的交點一個等分點),以原點為起點,圓的這
個
等分點為終點,作出
個向量。
其中幅角為正且最小的向量稱為 次單位向量,記為
。
(有沒有大佬幫我補張圖啊,畫不來)
其余的 個向量分別為
,它們可以由復數之間的乘法得來
。
容易看出 。
對於 ,它事實上就是
。
所以
關於單位根有兩個性質
性質一(又稱為折半引理):
證明一:
由幾何意義,這兩者表示的向量終點是一樣的。
證明二:
由計算的公式:
其實由此我們可以引申出
性質二(又稱為消去引理)
證明一:
由幾何意義,這兩者表示的向量終點是相反的,左邊較右邊在單位圓上多轉了半圈。
證明二:
由計算的公式:
最后一步由三角恆等變換得到。
0x04 離散傅里葉變換(Discrete Fourier Transform)
首先我們單獨考慮一個 項(
)的多項式
,其系數向量為
。我們將
次單位根的
~
次冪分別帶入
得到其點值向量
。
這個過程稱為離散傅里葉變換(Discrete Fourier Transform)。
如果朴素帶入,時間復雜度也是 的。
所以我們必須要利用到單位根 的特殊性質。
對於
考慮將其按照奇偶分組
令
則可得到
分類討論
設 ,
由上文提到的折半引理
對於
其中
由消去引理
故
注意, 與
取遍了
中的
個整數,保證了可以由這
個點值反推解出系數(上文已證明)。
於是我們可以知道
如果已知了 分別在
的取值,可以在
的時間內求出
的取值。
而 都是
一半的規模,顯然可以轉化為子問題遞歸求解。
時間復雜度:
0x05 離散傅里葉反變換(Inverse Discrete Fourier Transform)
使用快速傅里葉變換將點值表示的多項式轉化為系數表示,這個過程叫做離散傅里葉反變換(Inverse Discrete Fourier Transform)。
即由 維點值向量
推出
維系數向量
。
設 為
得到的離散傅里葉變換的結果。
我們構造一個多項式
設向量 中
為
在
的點值表示
即 ,
我們考慮對 進行還原
於是
由和式的性質
令
對其進行化簡
設
則
其公比為
當 即
時
此時
當 即
時
由等比數列求和公式
,此時
.
所以
將 帶入原式
所以 .
其中 為原多項式
的系數向量
中的
.
由此得到:
對於多項式 由插值節點
做離散傅里葉變換得到的點值向量
。我們將
作為插值節點,
作為系數向量,做一次離散傅里葉變換得到的向量每一項都除以
之后得到的
就是多項式的系數向量
。
注意到 是
的共軛復數。
這個過程稱為離散傅里葉反變換。
0x06 關於FFT在C++的實現
首先要解決復數運算的問題,我們可以使用C++STL自帶的 依照精度要求
一般為
。
也可以自己封裝,下面是我封裝的復數類。
struct Complex { double r, i ; Complex ( ) { } Complex ( double r, double i ) : r ( r ), i ( i ) { } inline void real ( const double& x ) { r = x ; } inline double real ( ) { return r ; } inline Complex operator + ( const Complex& rhs ) const { return Complex ( r + rhs.r, i + rhs.i ) ; } inline Complex operator - ( const Complex& rhs ) const { return Complex ( r - rhs.r, i - rhs.i ) ; } inline Complex operator * ( const Complex& rhs ) const { return Complex ( r * rhs.r - i * rhs.i, r * rhs.i + i * rhs.r ) ; } inline void operator /= ( const double& x ) { r /= x, i /= x ; } inline void operator *= ( const Complex& rhs ) { *this = Complex ( r * rhs.r - i * rhs.i, r * rhs.i + i * rhs.r ) ; } inline void operator += ( const Complex& rhs ) { r += rhs.r, i += rhs.i ; } inline Complex conj (