基4fft算法的蝶形圖_原地且自動整序的FFT算法

f40b062a57ac2b6ce9d1652f311c63c4.png

傳統的計算快速傅里葉變換的Cooley-Tukey算法效率極高,因其主要由蝶形運算構成,所以代碼形式也非常簡單,只是需要將輸入或者輸出按照位反轉的方式重新排序。

這個重新排序的步驟并不是必須的。Clive Temperton于1991年在Self-Sorting In-Place Fast Fourier Transforms一文中給出了適用于混合基數的原地FFT算法,不需要對輸入或輸出重新排序。本文將介紹這種算法的原理并給出基數2(Radix-2)情況下的具體構造和C++實現。作為FFT算法研究成果的集大成者,FFTW已應用了這種算法。

FFT7071專欄?fourier.v.ariant.cn
461f5233eeac7f06a674dfbb78bd56e8.png

預備:內存中的矩陣轉置

設x[t]為一個長度為M*N的向量,t也可表示為Ma+b。以M=5,N=3為例,x[0…14]={101, 102, 103,…, 115},如果將x視作一個5行3列的矩陣,那么a列b行的矩陣元素即是x[Ma+b]:

   a= 0,  1,  2
b=0 101 106 111
b=1 102 107 112
b=2 103 108 113
b=3 104 109 114
b=4 105 110 115

將這個矩陣轉置,不難發現轉置后的y[0…14]={101, 106, 111, 102,…, 110, 115}

   a= 0,  1,  2,  3,  4
b=0 101 102 103 104 105
b=1 106 107 108 109 110
b=2 111 112 113 114 115

y與x的關系是y[Nb+a]=x[Ma+b]。這個轉置變換也可以用一個置換矩陣P表示:y=Px。

展開:DFT變換式

記長度為

的信號為
的離散傅里葉變換,并且設:

展開DFT變換得到以下表達式:

其中

利用

用y代換x并繼續展開單位根的冪,其中

上式的求和等價于:

其中大括號內的求和相當于將y中的元素從地址0開始每相鄰的N個為一組總共M個長度為N的DFT。如果將y看作M行N列的矩陣,這是對每一列的變換,變換的結果依次乘以

,這時剩下的一個求和相當于對y的每一行單獨的DFT。

至此,長度為MN的變換分解為了長度為M和N的兩遍短變換。如果上式中不將第一遍DFT的結果乘以

,結果將是M*N的2維DFT。需要注意的是,變換y可以使上式的兩遍DFT均在原地進行,如果變換的是x,為保持變換結果的順序正確,必須以轉置的形式寫回第一遍短變換的結果。

題外話:將中間結果寫到另一處存儲區x',并且以x'為輸入做第二遍變換,結果寫回x,如此往復可以解決變換無法在原地進行的問題,這即是Stockham FFT算法。但是如此一來FFT需要額外的等于x長度的內存,即需要額外O(N)的空間。因為置換群中的元素均可分解為2置換,也就是對換的乘積,置換矩陣也可做如此分解,將轉置操作變換成一系列對換,從而可在原地進行,僅需要O(1)額外空間。然而轉置只能分解為數量巨大的對換,這種操作的效率不高。這也預示著,充分利用內存中數據的對換,可以保持O(1)的額外空間需求,同時使FFT在原地進行且順序正確。

展開:DFT變換矩陣和素因子算法

運用上文得出的分解到DFT的矩陣形式:

實際變換的是y(也就是轉置的x),第一遍DFT作于相鄰的N個元素(每列),將結果逐個乘以一個旋轉因子,再變換間隔為N的每組元素(每行),這個過程對應DFT矩陣的一種分解,也就是素因子分解FFT算法的基本構造:

其中W為下標相應長度的DFT矩陣;P為x[Ma+b]轉置到y[Nb+a]的置換矩陣;I為M或N階的單位矩陣;D為M*N階對角矩陣,第bN+d行對角線上的值為exp(2πibd/(MN))。?是矩陣的Kronecker積,定義如下:

例如:

Kronecker積滿足結合律:

滿足“混合乘積”性質:

為例:

繼續分解

運用“混合乘積”的性質將上式拆分為矩陣積:

對于長度為

的DFT矩陣分解,設:

可以得到:

這種分解正是時間抽取(DIT)基數2(Radix-2)的Cooley-Tukey算法,下文中只考慮此種FFT,頻率抽取(DIF)在附錄中討論。

已知T對應Cooley-Tukey算法每次迭代在整個輸入向量上的所有蝶形運算,上式中的

為2行8列到8行2列的矩陣轉置,作用是將x[2b+a]的值變換到x[8a+b],其中
。觀察8a+b和2b+a的二進制表示:
2^3 2^2 2^1 2^0
[b] [b] [b] [a] = 2b+a
[a] [b] [b] [b] = 8a+b

可知

的作用是將
的地址t二進制位向右環移1位。
的作用是保持t的最高1位不變,t的余下三位向右環移1位,因此經過所有的
變換:
2^3  2^2  2^1  2^0
[k3] [k2] [k1] [k0]
[k0] [k3] [k2] [k1] - P16
[k0] [k1] [k3] [k2] - P8
[k0] [k1] [k2] [k3] - P4

很明顯T之前所有

矩陣的乘積是輸入數據翻轉對應地址二進制位的置換矩陣。

對換:蝶形運算和對換

設DFT的長度為N,則x[t]中的t在二進制下有N位,定義

為對換
的置換矩陣,
對換二進制位中低位i和高位N-i-1得到。可以用一系列Q的乘積取代P的乘積:

的效果同樣完全反轉二進制位:
2^3  2^2  2^1  2^0
[k3] [k2] [k1] [k0]
[k0] [k2] [k1] [k3] - Q03
[k0] [k1] [k2] [k3] - Q12

在N=2M或2M+1的情況下:

轉置P無法簡單地在原地計算,而Q僅包含數量較少的對換,因此可以在原地完成。目前為止以T和Q組成的DFT矩陣W分解仍然表示先重排數據再開始蝶形運算的迭代,如果將T和Q結合起來,就能省略重排數據的操作,但是這要求T和Q可交換。為了證明這一點,首先需要求出Q的表達式。

觀察

翻轉二進制最高位和最低位的操作:
0000    0000
0001 -> 1000
0010    0010
0011 -> 1010
0100    0100
0101 -> 1100
0110    0110
0111 -> 1110
1000 -> 0001
1001    1001
1010 -> 0011
1011    1011
1100 -> 0101
1101    1101
1110 -> 0111
1111    1111

可以發現

的作用是將前一半數中的奇數
對換。因此:

這里

階置換矩陣,對換
交換二進制的最高位和最低位得到。

在為二進制數添加前綴的操作下

的作用是不變的:
00000    00000    10000    10000
00001 -> 01000    10001 -> 11000
00010    00010    10010    10010
00011 -> 01010    10011 -> 11010
00100    00100    10100    10100
00101 -> 01100    10101 -> 11100
00110    00110    10110    10110
00111 -> 01110    10111 -> 11110
01000 -> 00001    11000 -> 10001
01001    01001    11001    11001
01010 -> 00011    11010 -> 10011
01011    01011    11011    11011
01100 -> 00101    11100 -> 10101
01101    01101    11101    11101
01110 -> 00111    11110 -> 10111
01111    01111    11111    11111

因此對于N位二進制數:

為二進制數添加后綴的操作使

作用于全部后綴,可得出:

,現在可以將
展開:

因此對于所有的

可交換。這使
可以寫為:

已知一次蝶形運算的迭代

的作用是:將兩個長度為
的DFT結果作為奇偶兩部分,合并為長度為
的DFT結果,這樣的奇偶對共有
個。

中單個蝶形運算的偶、奇兩個輸入分別是
會將
對換。取
,則
將與
對換。在
的情況下,
分別是另一蝶形運算的偶、奇輸入。

可見

中,輸入數據地址相差
的兩個蝶形運算是成對的,第一個蝶形運算的奇數項輸入與第二個蝶形運算的偶數項輸入對換。如下圖所示:

c9cafb594e957431612d4864cfe4a5a4.png

下圖中畫出來N=16的基數2變換,輸入和輸出的順序均是正確的;圖中用顏色標出了某些蝶形運算,使蝶形運算的配對更清晰。注意其中成對蝶形運算的4個輸入輸出均在原地,并且與傳統Cooley-Tukey算法相比沒有計算量的差別。

19424ea3596c0a578fc8bd403edda118.png

下圖是作為對比的傳統Cooley-Tukey算法。

e2b75f96cb0f6acf57005e72dd23f352.png

附錄:本文算法的C++實現

/* copyright 2020, github.com/zhxxch, all rights reserved. */
#include <complex>
#include <iterator>
#include <cmath>
#include <cassert>
#include <cstddef>
template<typename iter_t>
#if __cplusplus > 201703L
requires std::random_access_iterator<iter_t>
#endifinline void fft_in_place(iter_t arr_begin,iter_t arr_end, const bool is_inverse) {using cplx_t = typename std::iterator_traits<iter_t>::value_type;using real_t = typename cplx_t::value_type;const size_t length= std::distance(arr_begin, arr_end);assert("requires length = pow(2,n)"&& (length & (length - 1)) == 0);constexpr real_t pi= 3.141592653589793238462643383;size_t sub_ft_size = 1;size_t num_sub_ft = length / sub_ft_size;size_t num_sub_ft_pair = num_sub_ft / 2;for(; sub_ft_size < num_sub_ft_pair;sub_ft_size *= 2, num_sub_ft /= 2,num_sub_ft_pair /= 2) {for(size_t coupled_group_pos = 0;coupled_group_pos < length;coupled_group_pos += 2 * num_sub_ft_pair) {for(size_t sub_ft_pos = coupled_group_pos;sub_ft_pos< coupled_group_pos + num_sub_ft_pair;sub_ft_pos += 2 * sub_ft_size) {for(size_t i = sub_ft_pos, nth_pow = 0;i < sub_ft_pos + sub_ft_size;i++, nth_pow += num_sub_ft_pair) {const cplx_t W = exp((is_inverse ? 1. : -1.)* cplx_t(0, 2 * nth_pow * pi)/ (real_t)length);const cplx_t parit00= arr_begin[i];const cplx_t parit01= arr_begin[num_sub_ft_pair+ i]* W;const cplx_t parit10= arr_begin[i + sub_ft_size];const cplx_t parit11= arr_begin[num_sub_ft_pair + i+ sub_ft_size]* W;arr_begin[i] = parit00 + parit01;arr_begin[i + sub_ft_size]= parit00 - parit01;arr_begin[num_sub_ft_pair + i]= parit10 + parit11;arr_begin[num_sub_ft_pair + i+ sub_ft_size]= parit10 - parit11;}}}}for(; sub_ft_size < length; sub_ft_size *= 2,num_sub_ft /= 2, num_sub_ft_pair /= 2) {for(size_t sub_ft_pos = 0; sub_ft_pos < length;sub_ft_pos += 2 * sub_ft_size) {for(size_t i = sub_ft_pos, nth_pow = 0;i < sub_ft_pos + sub_ft_size;i++, nth_pow += num_sub_ft_pair) {const cplx_t parit1= arr_begin[i + sub_ft_size]* exp((is_inverse ? 1. : -1.)* cplx_t(0, 2 * nth_pow * pi)/ (real_t)length);const cplx_t parit0 = arr_begin[i];arr_begin[i] = parit0 + parit1;arr_begin[i + sub_ft_size]= parit0 - parit1;}}}
}

使用方法-FFT

fft_in_place(arr.begin(), arr.end(), 0);

使用方法-IFFT

fft_in_place(arr.begin(), arr.end(), 1);

arr的長度必須是2的冪。

附錄:時間抽取與頻率抽取

為例,頻率抽取的FFT算法中:

換為使用Q表達的形式則為:

因此Q作用于蝶形運算的輸出。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537198.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537198.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537198.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

嵌入式BootLoader技術內幕(二)

三、Boot Loader 的主要任務與典型結構框架 在繼續本節的討論之前&#xff0c;首先我們做一個假定&#xff0c;那就是&#xff1a;假定內核映像與根文件系統映像都被加載到 RAM 中運行。之所以提出這樣一個假設前提是因為&#xff0c;在嵌入式系統中內核映像與根文件系統映像也…

MongoDB數據庫的遷移

最近公司開始要換服務器啦&#xff0c;MongoDB上面的數據又得遷移&#xff0c;還是記錄一下比較好。 1&#xff09;、將MongoDB的壓縮包解壓至相對應的路徑(壓縮文件在本地服務器的地址192.168.0.22的/opt/zip文件下面) 2&#xff09;、配置好mongodb.conf文件&#xff0c;配…

excel vba 如何將日期周幾轉換成文字_這5個超實用的Excel技巧,讓你的辦公效率更高...

導讀&#xff1a;對于辦公職員來說&#xff0c;Excel是幾乎每天都會接觸的辦公軟件。在Excel中&#xff0c;有非常多的小技巧&#xff0c;學習這些小技巧需要不斷的積累和應用&#xff0c;今天指北針就來給大家分享5個超實用的Excel技巧&#xff0c;讓辦公變得更加有效率。文/芒…

VMware創建Linux及局域網內獨立訪問IP和訪問外網IP的配置

好早之前有一篇是配置遠程連接Linux和部署Tomcat的文章&#xff0c;但是并沒有講解如何配置IP的相關知識。最近公司在搞集群配置&#xff0c;我就先拿電腦上的VMware上的Linux做個測試&#xff0c;分享和總結一下經驗吧&#xff0c;也算是為了補齊之前的那個空白&#xff01; …

每位設計師都應該擁有的50個CSS代碼片段

每位設計師都應該擁有的50個CSS代碼片段

C#淺拷貝與深拷貝區別

也許會有人這樣解釋C# 中淺拷貝與深拷貝區別&#xff1a; 淺拷貝是對引用類型拷貝地址&#xff0c;對值類型直接進行拷貝。 不能說它完全錯誤&#xff0c;但至少還不夠嚴謹。比如&#xff1a;string 類型咋說&#xff1f; 其實&#xff0c;我們可以通過實踐來尋找答案。 首先&a…

內網安裝nginx+keepalived環境配置及簡單使用

分享一下這次艱難的配置過程&#xff0c;銜接上一篇的配置內網獨立IP虛擬機。 先吐槽一波&#xff0c;由于公司網絡屬于內網&#xff0c;與外網互不相通&#xff0c;所以在安裝nginx的時候可能會去外網找相對應rpm文件&#xff0c;而且也有許多的版本不兼容問題&#xff0c;好…

cad連續標注數字123怎么弄_實例講解CAD模型與布局中的各種比例

好課推薦&#xff1a;零基礎CAD&#xff1a;點我CAD室內&#xff1a;點我 周站長CAD&#xff1a;點我CAD機械&#xff1a;點我 Bim教程&#xff1a;點我CAD建筑&#xff1a;點我CAD三維&#xff1a;點我全屋定制&#xff1a;點我 ps教程&#xff1a;點我蘋果版CAD:點我 3dmax教…

SpringMvc異步請求的使用及部分原理

最近隔壁項目組的項目又出問題了&#xff0c;一直被用戶投訴太卡了&#xff0c;頁面白屏的那種&#xff0c;打開源代碼一看&#xff0c;全是非異步請求&#xff0c;類似于以下寫法&#xff1a; ResponseBodyRequestMapping(value "/getTest")public String getTest(…

Microsoft BizTalk ESB Toolkit 2.0

[>>> 更多<BizTalk開發系列>文章 ] 微軟于6月8號發布了BizTalk Server 2009企業集成平臺的最后一個功能組件:ESB Toolkit 2.0 (原名:ESB Guidance 2.0)&#xff0c;ESB ToolKit 2.0一個是工具和代碼集擴展了BizTalk Server 2009對于松耦合和動態消息架構的支持…

python解釋器環境中用于表示上一次運算結果的特殊變量_判斷正誤 PUSH CL_學小易找答案...

【單選題】將數學關系式2 【填空題】請用4位十六進制寫出每條指令結束后AX的值。 MOV AX, 0 DEC AX ADD AX, 7FFFH ADC AX, 1 NEG AX OR AX, 3FDFH AND AX, 0EBEDH XCHG AH, AL SAL AX, 1 RCL AX, 1 【判斷題】判斷正誤 MOV DX, 09H 【判斷題】判斷正誤 MOV [1200H], [SI] 【單…

Java線程的使用及共享協作

創建線程的三種方式 1、繼承Thread&#xff1b; static class MyThread extends Thread{Overridepublic void run() {//do something...} } public static void main(String[] args) throws InterruptedException {MyThread thread new MyThread ();thread.start(); } 2、實…

WCF學習筆記(三):開啟net.tcp端口

正在做一個使用tcp協議的WCF示例&#xff0c;遇到很多問題。首當其沖的問題就是——如何為WCF打開tcp端口。。。 具體步驟如下&#xff1a; 1、在IIS中為WCF安裝支持TCP協議的組件&#xff1a; 2、在防火墻的入棧規則中開啟808端口&#xff1b; 3、在servies.msc中打開兩個服務…

孿生神經網絡_軒轅實驗室:數字孿生:基于機器學習的汽車數字孿生模型

本文來源&#xff1a;A. Rassolkin, T. Vaimann, A. Kallaste, and V. Kuts, “Digital twin for propulsion drive of autonomous electric vehicle,” in 2019 IEEE 60th International Scientific Conference on Power and Electrical Engineering of Riga Technical Univer…

Java線程Fork/Join思想及實現

最近在看線程這一塊的東西&#xff0c;所以之前的那篇文章就是用來記錄的&#xff0c;但看起來好簡單的樣子&#xff0c;哈哈哈&#xff01; 這兩天看的是Fork/Join 分而治之的思想&#xff0c;Doug Lea大師的JUC還是挺強的&#xff0c;學并發編程應該沒有人不知道這個大佬吧&…

Sgen.exe: Speed up XmlSerializer's Startup Performance [.NET 2.0, XML Serialization]

Sgen.exe: Speed up XmlSerializers Startup Performance [.NET 2.0, XML Serialization] Written by Allen Lee 1. Why Sgen.exe? 在《Serialize Your Deck with Positron [XML Serialization, XSD, C#]》一文中&#xff0c;我們領略到 XML Serialization 是如何簡化我們的 X…

Java線程并發常用工具類使用

這次整理了一些比較常用的線程工具類啦。 CountDownLatch&#xff1a;在一組線程執行完后&#xff0c;才能開始執行調用等待的線程。上片文章提到過junit的測試盡量不要測試線程&#xff0c;如果硬是要可以使用CountDownLatch進行測試 CyclicBarrier&#xff1a;在一組線程中…

三維圖形幾何變換算法實驗_計算機視覺方向簡介 | 深度學習視覺三維重建

點擊上方“計算機視覺life”&#xff0c;選擇“星標”快速獲得最新干貨作者&#xff1a; Moonsmilehttps://zhuanlan.zhihu.com/p/79628068本文已由作者授權&#xff0c;未經允許&#xff0c;不得二次轉載三維重建意義三維重建作為環境感知的關鍵技術之一&#xff0c;可用于自動…

讀《高效程序員的45個習慣——敏捷開發修煉之道》

本書主要用平易的語言講述了45個有助于提高程序員自身敏捷的習慣&#xff0c;個人感覺這種老外寫的書翻譯成中文就少了很多意思。 主要的45個習慣是&#xff1a; 做事欲速則不達對事不對人排除萬難跟蹤變化對團隊投資懂得丟棄打破沙鍋問到底把握開發節奏讓客戶做決定讓設計指導…

Java線程CAS原子操作

這次分享一些關于原子操作(CAS)的東西. 定義 CAS(Compare And Swap)是CPU的一個指令級別的操作&#xff0c;叫原子操作&#xff0c;原子操作是不可分割的&#xff0c;跟事務差不多&#xff0c;要么全部執行完成&#xff0c;要么不執行&#xff1b; 像這種操作有點類似阻塞鎖…