本篇目的:
1)回顧一下 基2-快速傅立葉變換(radix2-FFT) 的理論推導(dǎo);
2)以C++語(yǔ)言用最直白的方式實(shí)現(xiàn) 基2-快速傅立葉變換踱阿。
例行推導(dǎo)
我們考慮的 基 2-FFT 需要數(shù)據(jù)長(zhǎng)度為??形式管钳,其中?。
離散傅立葉變換 DFT 公式如下:
其中:為 1 的次單位根软舌;計(jì)算這個(gè)變換系數(shù)需要??次復(fù)數(shù)乘法才漆,和次復(fù)數(shù)加法。為了得到一個(gè)高效的算法佛点,我們作出以下變換栽烂。
其中:我們用到了
????????到此,DFT 運(yùn)算的復(fù)數(shù)乘法量 不減反增恋脚;不過(guò)這里蘊(yùn)含的“重復(fù)計(jì)算”不是一眼就能看出來(lái)的腺办,找到這樣的”重復(fù)計(jì)算“就是 快速傅立葉變換 的核心所在。下面我們將利用 旋轉(zhuǎn)因子??的以下特殊性質(zhì)糟描,找到重復(fù)的乘法運(yùn)算怀喉,并將其優(yōu)化掉:
1)周期性:
2)斜對(duì)稱(chēng)性:
????????如果我們?cè)谏希?)式中,將??的取值范圍限制為船响,則(1)式中的奇偶部分可以分別視為?奇數(shù)下標(biāo)子序列 和 偶數(shù)下標(biāo)子序列 的長(zhǎng)度均為??的 DFT 躬拢。令,见间,聊闯,得到子序列的 DFT:
于是完整序列的 DFT 的前半部分系數(shù)可以表示如下:
下面計(jì)算完整序列的 DFT 的后半部分系數(shù)?;為了清晰米诉,我們重新算一遍:
到此菱蔬,我們得到了最重要的結(jié)果:
????????而這就是著名的 蝶形運(yùn)算 ,可以看到我們只用了一次復(fù)數(shù)乘法(兩次加法)史侣,就得到了兩個(gè) DFT 系數(shù)拴泌,而這也是 FFT 的核心。在上(2)式中惊橱,如果使用樸素方法直接計(jì)算蚪腐,則共需要?次復(fù)數(shù)乘法運(yùn)算。所以税朴,經(jīng)過(guò)一次 奇偶拆分 回季,我們直接將計(jì)算復(fù)雜度降低了約一半家制。
? ? ? ? 在我們的約定下,于是當(dāng)我們想要計(jì)算時(shí)泡一,我們不妨繼續(xù)對(duì)分別進(jìn)行奇偶拆分慰丛,這樣的拆分過(guò)程在 DSP 領(lǐng)域有個(gè)專(zhuān)門(mén)的術(shù)語(yǔ)叫 Decimation In Time(DIT)。這樣的過(guò)程可以進(jìn)行??次瘾杭,于是我們就得到了 radix2-DIT-FFT 算法過(guò)程诅病。顯然,每一層我們只需要??次復(fù)數(shù)乘法粥烁,共層贤笆,于是復(fù)數(shù)乘法量降低為。
? ? ? ? 實(shí)現(xiàn)這個(gè)過(guò)程有兩種方法:1)使用 遞歸法 逐層對(duì)序列進(jìn)行 奇偶抽忍肿琛芥永;2)使用 迭代法 從最底層前向推進(jìn)。我們嘗試使用 迭代法 來(lái)實(shí)現(xiàn)代碼钝吮,不過(guò)在這之前埋涧,我們還需要分析在每層中數(shù)據(jù)的排列;如下圖 (1)奇瘦,對(duì)于一個(gè) 8 點(diǎn) FFT 我們會(huì)有 3 層抽取過(guò)程棘催,最終的數(shù)據(jù)排列為 0,4,2,6,1,5,3,7;那么如何在實(shí)際中確定長(zhǎng)度為 N 的樣本點(diǎn)的排列順序呢耳标?偉大的前人發(fā)現(xiàn)當(dāng)我們把 下標(biāo) 按 2 進(jìn)制進(jìn)行展開(kāi)時(shí)醇坝,則重排的序列中每個(gè)元素的下標(biāo)剛好就是該元素在原來(lái)序列中的下標(biāo) 按位逆序 得到(如下圖 2 )。
矩陣運(yùn)算視角
例如當(dāng)時(shí)次坡,我們可以使用的 線性變換 來(lái)表示:
我們把中間的矩陣詳細(xì)寫(xiě)開(kāi):
再次由 周期性?和 斜對(duì)稱(chēng)性呼猪,我們來(lái)對(duì)上面的矩陣作一些化簡(jiǎn):
? ? ? ? 暫時(shí),讓我們忘掉前面的 例行推導(dǎo)?的結(jié)論砸琅,重新用矩陣乘法視角來(lái)看看 傅立葉變換 過(guò)程宋距。
????????我們注意到該矩陣內(nèi)部包含了大量的重復(fù)元素,那么是否可以利用這一現(xiàn)象減少一些“重復(fù)計(jì)算“呢症脂?前面已經(jīng)給出了肯定的答案谚赎;我們繼續(xù)以”事后諸葛亮“的視角分析一下矩陣。
? ? ? ? 我們把(1)式記為向量符號(hào)形式:摊腋,設(shè)為任意置換矩陣沸版,由線性代數(shù)知識(shí)可知:其右乘矩陣相當(dāng)于對(duì)矩陣的列向量作相應(yīng)的排列,左乘一個(gè)矩陣相當(dāng)于對(duì)矩陣的行向量作相應(yīng)的排列兴蒸;并且將置換矩陣?兩次?右乘到矩陣??相當(dāng)于對(duì)列向量換過(guò)去又換回來(lái),也就是细办,于是我們得到:
? ? ? ? 下面我們令??是由單位矩陣??經(jīng)過(guò)如下變換得到的矩陣:交換??的第 1 行和第 4 行橙凳,交換第 3 行和第 6 行蕾殴。至于為什么矩陣??是這樣的,因?yàn)槲艺f(shuō)過(guò)這是 “事后諸葛亮”岛啸,其實(shí)這就是 “按位逆序法 bit reversing order” 得到的钓觉。如此得到下面的按列向量重排過(guò)的矩陣?,當(dāng)然 數(shù)據(jù)向量??的行也按同樣的規(guī)律重排過(guò)了坚踩。
? ? ? ? 其中:荡灾,觀察該分塊矩陣,我們發(fā)現(xiàn):
1)瞬铸;
2)的每一列由的對(duì)應(yīng)的列 乘以 對(duì)應(yīng)的因子得到的批幌;
3)的每一列由的對(duì)應(yīng)的列?乘以 對(duì)應(yīng)的因子 得到的;
? ? ? ? 簡(jiǎn)化一下符號(hào)嗓节,設(shè)荧缘;用矩陣語(yǔ)言來(lái)表達(dá)上面的描述,如下:
同理拦宣,記:
其中:截粗,
繼續(xù)作分解:
再次,利用 分塊對(duì)角矩陣 乘法的便利性鸵隧,得到:
代入上面(2)式中得:
將代入上式整理后得到:
其中:分別為 2 階和 4 階單位矩陣凿掂,限于空間和美觀度,這里就不展開(kāi)了舀凛;并且我們將上面的矩陣 從右到左 依次命令為种蝶。
? ? ? ? 將上面的展開(kāi)式 左乘 置換后的數(shù)據(jù)向量??,我們就得到了 快速傅立葉變換的 過(guò)程靡羡;我們將上面的展開(kāi)式中的矩陣系洛,依次 從右到左 用流程圖畫(huà)出來(lái),就得到下面的變換圖:(其中W^n 表示略步,沒(méi)有明確寫(xiě)出來(lái)的權(quán)重均為 1)
由于 簡(jiǎn)書(shū)平臺(tái) 的富文本格式不能同時(shí)支持“Latex符號(hào)”和“代碼塊嵌入”描扯,所以我將 C++ 的實(shí)現(xiàn)放到?下一篇?中。