小学生都能看懂的FFT!!!
小學(xué)生都能看懂的FFT!!!
前言
在創(chuàng)新實(shí)踐中心偷偷看了一天FFT資料后,我終于看懂了一點(diǎn)。為了給大家提供一份簡(jiǎn)單易懂的學(xué)習(xí)資料,同時(shí)也方便自己以后復(fù)習(xí),我決定動(dòng)手寫(xiě)這份學(xué)習(xí)筆記。
食用指南:
本篇受眾:如標(biāo)題所示,另外也面向同我一樣高中起步且非常菜的OIer。真正的dalao請(qǐng)無(wú)視。
本篇目標(biāo):讓大家(和不知道什么時(shí)候把FFT忘了的我)在沒(méi)有數(shù)學(xué)基礎(chǔ)的情況下,以最快的速度了解并 會(huì)寫(xiě) FFT。因此本篇將采用盡可能通俗易懂的語(yǔ)言,且略過(guò)大部分?jǐn)?shù)學(xué)證明,在嚴(yán)謹(jǐn)性上可能有欠缺。但如果您發(fā)現(xiàn)了較大的邏輯漏洞,歡迎在評(píng)論里指正!
最后……來(lái)個(gè)版權(quán)聲明吧。本文作者胡小兔,博客地址http://rabbithu.cnblogs.com。暫未許可在任何其他平臺(tái)轉(zhuǎn)載。
你一定聽(tīng)說(shuō)過(guò)FFT,它的高逼格名字讓人望而卻步——“快速傅里葉變換”。
你可能知道它可以\(O(n \log n)\)求高精度乘法,你想學(xué),可是面對(duì)一堆公式,你無(wú)從下手。
那么歡迎閱讀這篇教程!
[Warning] 本文涉及復(fù)數(shù)(虛數(shù))的一小部分內(nèi)容,這可能是最難的部分,但只要看下去也不是非常難,請(qǐng)不要看到它就中途退出啊QAQ。
什么是FFT?
快速傅里葉變換(FFT)是一種能在\(O(n \log n)\)的時(shí)間內(nèi)將一個(gè)多項(xiàng)式轉(zhuǎn)換成它的點(diǎn)值表示的算法。
補(bǔ)充資料:什么是點(diǎn)值表示
設(shè)\(A(x)\)是一個(gè)\(n - 1\)次多項(xiàng)式,那么把\(n\)個(gè)不同的\(x\)代入,會(huì)得到\(n\)個(gè)\(y\)。這\(n\)對(duì)\((x, y)\)唯一確定了該多項(xiàng)式,即只有一個(gè)多項(xiàng)式能同時(shí)滿(mǎn)足“代入這些\(x\),得到的分別是這些\(y\)”。
由多項(xiàng)式可以求出其點(diǎn)值表示,而由點(diǎn)值表示也可以求出多項(xiàng)式。
(并不想證明,十分想看證明的同學(xué)請(qǐng)前往“參考資料”部分)。
注:下文如果不加特殊說(shuō)明,默認(rèn)所有\(n\)為2的整數(shù)次冪。如果一個(gè)多項(xiàng)式次數(shù)不是2的整數(shù)次冪,可以在后面補(bǔ)0。
為什么要使用FFT?
FFT可以用來(lái)加速多項(xiàng)式乘法(平時(shí)非常常用的高精度大整數(shù)乘法就是最終把\(x = 10\)代入的多項(xiàng)式乘法)。
假設(shè)有兩個(gè)\(n-1\)次多項(xiàng)式\(A(x)\)和\(B(x)\),我們的目標(biāo)是——把它們乘起來(lái)。
普通的多項(xiàng)式乘法是\(O(n^2)\)的——我們要枚舉\(A(x)\)中的每一項(xiàng),分別與\(B(x)\)中的每一項(xiàng)相乘,來(lái)得到一個(gè)新的多項(xiàng)式\(C(x)\)。
但有趣的是,兩個(gè)用點(diǎn)值表示的多項(xiàng)式相乘,復(fù)雜度是\(O(n)\)的!具體方法:\(C(x_i) = A(x_i) \times B(x_i)\),所以\(O(n)\)枚舉\(x_i\)即可。
要是我們把兩個(gè)多項(xiàng)式轉(zhuǎn)換成點(diǎn)值表示,再相乘,再把新的點(diǎn)值表示轉(zhuǎn)換成多項(xiàng)式豈不就可以\(O(n)\)解決多項(xiàng)式乘法了!
……很遺憾,顯然,把多項(xiàng)式轉(zhuǎn)換成點(diǎn)值表示的樸素算法是\(O(n^2)\)的。另外,即使你可能不會(huì)——把點(diǎn)值表示轉(zhuǎn)換為多項(xiàng)式的樸素“插值算法”也是\(O(n^2)\)的。
難道大整數(shù)乘法就只能是\(O(n^2)\)嗎?!不甘心的同學(xué)可以發(fā)現(xiàn),大整數(shù)乘法復(fù)雜度的瓶頸可能在“多項(xiàng)式轉(zhuǎn)換成點(diǎn)值表示”這一步(以及其反向操作),只要完成這一步就可以\(O(n)\)求答案了。如果能優(yōu)化這一步,豈不美哉?
傅里葉:這個(gè)我會(huì)!
離散傅里葉變換(快速傅里葉變換的樸素版)
傅里葉發(fā)明了一種辦法:規(guī)定點(diǎn)值表示中的\(n\)個(gè)\(x\)為\(n\)個(gè)模長(zhǎng)為\(1\)的復(fù)數(shù)。
——等等,先別看到復(fù)數(shù)就走!
補(bǔ)充資料:什么是復(fù)數(shù)
如果你學(xué)過(guò)復(fù)數(shù),這段不用看了;
如果你學(xué)過(guò)向量,請(qǐng)把復(fù)數(shù)理解成一個(gè)向量;
如果你啥都沒(méi)學(xué)過(guò),請(qǐng)把復(fù)數(shù)理解成一個(gè)平面直角坐標(biāo)系上的點(diǎn)。
復(fù)數(shù)具有一個(gè)實(shí)部和一個(gè)虛部,正如一個(gè)向量(或點(diǎn))有一個(gè)橫坐標(biāo)和一個(gè)縱坐標(biāo)。例如復(fù)數(shù)\(3 + 2i\),實(shí)部是\(3\),虛部是\(2\),\(i = \sqrt{-1}\)。可以把它想象成向量\((3, 2)\)或點(diǎn)\((3, 2)\)。
但復(fù)數(shù)比一個(gè)向量或點(diǎn)更妙的地方在于——復(fù)數(shù)也是一種數(shù),它可以像我們熟悉的實(shí)數(shù)那樣進(jìn)行加減乘除等運(yùn)算,還可以代入多項(xiàng)式\(A(x)\)——顯然你不能把一個(gè)向量或點(diǎn)作為\(x\)代入進(jìn)去。
復(fù)數(shù)相乘的規(guī)則:模長(zhǎng)相乘,幅角相加。模長(zhǎng)就是這個(gè)向量的模長(zhǎng)(或是這個(gè)點(diǎn)到原點(diǎn)的距離);幅角就是x軸正方向逆時(shí)針旋轉(zhuǎn)到與這個(gè)向量共線(xiàn)所途徑的角(或是原點(diǎn)出發(fā)、指向x軸正方向的射線(xiàn)逆時(shí)針旋轉(zhuǎn)至過(guò)這個(gè)點(diǎn)所經(jīng)過(guò)的角)。想學(xué)會(huì)FFT,“模長(zhǎng)相乘”暫時(shí)不需要了解過(guò)多,但“幅角相加”需要記住。
C++的STL提供了復(fù)數(shù)模板!
頭文件:#include <complex>
定義: complex<double> x;
運(yùn)算:直接使用加減乘除。
傅里葉要用到的\(n\)個(gè)復(fù)數(shù),不是隨機(jī)找的,而是——把單位圓(圓心為原點(diǎn)、1為半徑的圓)\(n\)等分,取這\(n\)個(gè)點(diǎn)(或點(diǎn)表示的向量)所表示的虛數(shù),即分別以這\(n\)個(gè)點(diǎn)的橫坐標(biāo)為實(shí)部、縱坐標(biāo)為虛部,所構(gòu)成的虛數(shù)。
從點(diǎn)\((1, 0)\)開(kāi)始(顯然這個(gè)點(diǎn)是我們要取的點(diǎn)之一),逆時(shí)針將這\(n\)個(gè)點(diǎn)從\(0\)開(kāi)始編號(hào),第\(k\)個(gè)點(diǎn)對(duì)應(yīng)的虛數(shù)記作\(\omega_n^k\)(根據(jù)復(fù)數(shù)相乘時(shí)模長(zhǎng)相乘幅角相加可以看出,\(\omega_n^k\)是\(\omega_n^1\)的\(k\)次方,所以\(\omega_n^1\)被稱(chēng)為\(n\)次單位根)。
根據(jù)每個(gè)復(fù)數(shù)的幅角,可以計(jì)算出所對(duì)應(yīng)的點(diǎn)/向量。\(\omega_n^k\)對(duì)應(yīng)的點(diǎn)/向量是\((\cos \frac{k}{n}2\pi, \sin \frac{k}{n}2\pi)\),也就是說(shuō)這個(gè)復(fù)數(shù)是\(\cos \frac{k}{n}2\pi + i\sin \frac{k}{n}2\pi\)。
傅里葉說(shuō):把\(n\)個(gè)復(fù)數(shù)\(\omega_n^0, \omega_n^1, \omega_n^2, ..., \omega_n^{n-1}\)代入多項(xiàng)式,能得到一種特殊的點(diǎn)值表示,這種點(diǎn)值表示就叫離散傅里葉變換吧!
[Warning] 從現(xiàn)在開(kāi)始,本文個(gè)別部分會(huì)集中出現(xiàn)數(shù)學(xué)公式,但是都不是很難,公式恐懼癥患者請(qǐng)堅(jiān)持!Stay Determined!
補(bǔ)充資料:單位根的性質(zhì)
性質(zhì)一:\(\omega_{2n}^{2k} = \omega_{n}^{k}\)
證明:它們對(duì)應(yīng)的點(diǎn)/向量是相同的。
性質(zhì)二:\(\omega_{n}^{k + \frac{n}{2}} = -\omega_{n}^{k}\)
證明:它們對(duì)應(yīng)的點(diǎn)是關(guān)于原點(diǎn)對(duì)稱(chēng)的(對(duì)應(yīng)的向量是等大反向的)。
為什么要使用單位根作為\(x\)代入
當(dāng)然是因?yàn)殡x散傅里葉變換有著特殊的性質(zhì)啦。
[Warning] 下面有一些證明,如果不想看,請(qǐng)?zhí)郊哟值摹耙粋€(gè)結(jié)論”部分。
設(shè)\((y_0, y_1, y_2, ..., y_{n - 1})\)為多項(xiàng)式\(A(x) = a_0 + a_1x + a_2x^2 +...+a_{n-1}x^{n-1}\)的離散傅里葉變換。
現(xiàn)在我們?cè)僭O(shè)一個(gè)多項(xiàng)式\(B(x) = y_0 + y_1x + y_2x^2 +...+y_{n-1}x^{n-1}\),現(xiàn)在我們把上面的\(n\)個(gè)單位根的倒數(shù),即\(\omega_{n}^{0}, \omega_{n}^{-1}, \omega_{n}^{-2}, ..., \omega_{n}^{-(n - 1)}\)作為\(x\)代入\(B(x)\), 得到一個(gè)新的離散傅里葉變換\((z_0, z_1, z_2, ..., z_{n - 1}\))。
\[ \begin{align*} z_k &= \sum_{i = 0}^{n - 1} y_i(\omega_n^{-k})^i \\ &= \sum_{i = 0}^{n - 1}(\sum_{j = 0}^{n - 1} a_j(\omega_n^i)^j)(\omega_n^{-k})^i \\ &= \sum_{j = 0}^{n - 1}a_j(\sum_{i = 0}^{n - 1}(\omega_n^{j - k})^i) \end{align*} \]
這個(gè)\(\sum_{i = 0}^{n - 1}(\omega_n^{j - k})^i\)是可求的:當(dāng)\(j - k = 0\)時(shí),它等于\(n\); 其余時(shí)候,通過(guò)等比數(shù)列求和可知它等于\(\frac{(\omega_n^{j - k})^n - 1}{\omega_n^{j - k} - 1} = \frac{(\omega_n^n)^{j - k} - 1}{\omega_n^{j - k} - 1} = \frac{1^{j - k}- 1}{\omega_n^{j - k} - 1} = 0\)。
那么,\(z_k\)就等于\(na_k\), 即:
\[a_i = \frac{z_i}{n}\]
一個(gè)結(jié)論
把多項(xiàng)式\(A(x)\)的離散傅里葉變換結(jié)果作為另一個(gè)多項(xiàng)式\(B(x)\)的系數(shù),取單位根的倒數(shù)即\(\omega_{n}^{0}, \omega_{n}^{-1}, \omega_{n}^{-2}, ..., \omega_{n}^{-(n - 1)}\)作為\(x\)代入\(B(x)\),得到的每個(gè)數(shù)再除以n,得到的是\(A(x)\)的各項(xiàng)系數(shù)。這實(shí)現(xiàn)了傅里葉變換的逆變換——把點(diǎn)值表示轉(zhuǎn)換成多項(xiàng)式系數(shù)表示,這就是離散傅里葉變換神奇的特殊性質(zhì)。
快速傅里葉變換
雖然傅里葉發(fā)明了神奇的變換,能把多項(xiàng)式轉(zhuǎn)換成點(diǎn)值表示又轉(zhuǎn)換回來(lái),但是……它仍然是暴力代入的做法,復(fù)雜度仍然是\(O(n^2)\)啊!(傅里葉:我都沒(méi)見(jiàn)過(guò)計(jì)算機(jī),我干啥要優(yōu)化復(fù)雜度……)
于是,快速傅里葉變換應(yīng)運(yùn)而生。它是一種分治的傅里葉變換。
[Warning] 下面有較多公式。看起來(lái)很?chē)樔?#xff0c;但是并不復(fù)雜。請(qǐng)堅(jiān)持看完。
快速傅里葉變換的數(shù)學(xué)證明
仍然,我們?cè)O(shè)\(A(x) = a_0 + a_1x + a_2x^2 +...+a_{n-1}x^{n-1}\),現(xiàn)在為了求離散傅里葉變換,要把一個(gè)\(x = \omega_n^k\)代入。
考慮將\(A(x)\)的每一項(xiàng)按照下標(biāo)的奇偶分成兩部分:
\[A(x) = (a_0 + a_2x^2 + ... + a_{n - 2}x^{n - 2}) + (a_1x + a_3x^3 + ... + a_{n-1}x^{n-1})\]
設(shè)兩個(gè)多項(xiàng)式:
\[A_1(x) = a_0 + a_2x + ... + a_{n - 2}x^{\frac{n}{2} - 1}\]
\[A_2(x) = a_1 + a_3x + ... + a_{n - 1}x^{\frac{n}{2} - 1}\]
則:
\[A(x) = A_1(x^2) + xA_2(x^2)\]
假設(shè)\(k < \frac{n}{2}\),現(xiàn)在要把\(x = \omega_n^k\)代入:
\[\begin{align*} A(\omega_n^k) &= A_1(\omega_n^{2k}) + \omega_n^kA_2(\omega_n^{2k}) \\ &= A_1(\omega_{\frac{n}{2}}^{k}) + \omega_n^kA_2(\omega_{\frac{n}{2}}^{k}) \end{align*}\]
那么對(duì)于\(A(\omega_n^{k + \frac{n}{2}})\):
\[\begin{align*} A(\omega_n^{k + \frac{n}{2}}) &= A_1(\omega_n^{2k + n}) + \omega_n^{k + \frac{n}{2}}A_2(\omega_n^{2k + n}) \\ &= A_1(\omega_{\frac{n}{2}}^{k} \times \omega_n^n) + \omega_n^{k + \frac{n}{2}} A_2(\omega_{\frac{n}{2}}^{k} \times \omega_n^n) \\ &= A_1(\omega_{\frac{n}{2}}^{k}) - \omega_n^kA_2(\omega_{\frac{n}{2}}^{k}) \end{align*}\]
所以,如果我們知道兩個(gè)多項(xiàng)式\(A_1(x)\)和\(A_2(x)\)分別在\((\omega_{\frac{n}{2}}^{0}, \omega_{\frac{n}{2}}^{1}, \omega_{\frac{n}{2}}^{2}, ... , \omega_{\frac{n}{2}}^{\frac{n}{2} - 1}\))的點(diǎn)值表示,就可以\(O(n)\)求出\(A(x)\)在\(\omega_n^0, \omega_n^1, \omega_n^2, ..., \omega_n^{n-1}\)處的點(diǎn)值表示了。而\(A_1(x)\)和\(A_2(x)\)都是規(guī)模縮小了一半的子問(wèn)題。分治邊界是\(n = 1\),此時(shí)直接return。
快速傅里葉變換的實(shí)現(xiàn)
寫(xiě)個(gè)遞歸就可以實(shí)現(xiàn)一個(gè)FFT了!
cp omega(int n, int k){return cp(cos(2 * PI * k / n), sin(2 * PI * k / n)); } void fft(cp *a, int n, bool inv){if(n == 1) return;static cp buf[N];int m = n / 2;for(int i = 0; i < m; i++){ //將每一項(xiàng)按照奇偶分為兩組buf[i] = a[2 * i];buf[i + m] = a[2 * i + 1];}for(int i = 0; i < n; i++)a[i] = buf[i];fft(a, m, inv); //遞歸處理兩個(gè)子問(wèn)題fft(a + m, m, inv);for(int i = 0; i < m; i++){ //枚舉x,計(jì)算A(x)cp x = omega(n, i); if(inv) x = conj(x); //conj是一個(gè)自帶的求共軛復(fù)數(shù)的函數(shù),精度較高。當(dāng)復(fù)數(shù)模為1時(shí),共軛復(fù)數(shù)等于倒數(shù)buf[i] = a[i] + x * a[i + m]; //根據(jù)之前推出的結(jié)論計(jì)算buf[i + m] = a[i] - x * a[i + m];}for(int i = 0; i < n; i++)a[i] = buf[i]; }inv表示這次用的單位根是否要取倒數(shù)。
至此你已經(jīng)會(huì)寫(xiě)fft了!但是這個(gè)fft還是1.0版本,比較慢(可能同時(shí)還比較長(zhǎng)?),親測(cè)可能會(huì)比加了一些優(yōu)化的fft慢了4倍左右……
那么我們來(lái)學(xué)習(xí)一些優(yōu)化吧!
優(yōu)化fft
非遞歸fft
在進(jìn)行fft時(shí),我們要把各個(gè)系數(shù)不斷分組并放到兩側(cè),那么一個(gè)系數(shù)原來(lái)的位置和最終的位置有什么規(guī)律呢?
初始位置:0 1 2 3 4 5 6 7
第一輪后:0 2 4 6|1 3 5 7
第二輪后:0 4|2 6|1 5|3 7
第三輪后:0|4|2|6|1|5|3|7
“|”代表分組界限。
可以發(fā)現(xiàn)(這你都能發(fā)現(xiàn)?),一個(gè)位置a上的數(shù),最后所在的位置是“a二進(jìn)制翻轉(zhuǎn)得到的數(shù)”,例如6(011)最后到了3(110),1(001)最后到了4(100)。
那么我們可以據(jù)此寫(xiě)出非遞歸版本fft:先把每個(gè)數(shù)放到最后的位置上,然后不斷向上還原,同時(shí)求出點(diǎn)值表示。
代碼:
cp a[N], b[N], omg[N], inv[N];void init(){for(int i = 0; i < n; i++){omg[i] = cp(cos(2 * PI * i / n), sin(2 * PI * i / n));inv[i] = conj(omg[i]);} } void fft(cp *a, cp *omg){int lim = 0;while((1 << lim) < n) lim++;for(int i = 0; i < n; i++){int t = 0;for(int j = 0; j < lim; j++)if((i >> j) & 1) t |= (1 << (lim - j - 1));if(i < t) swap(a[i], a[t]); // i < t 的限制使得每對(duì)點(diǎn)只被交換一次(否則交換兩次相當(dāng)于沒(méi)交換)}static cp buf[N];for(int l = 2; l <= n; l *= 2){int m = l / 2;for(int j = 0; j < n; j += l)for(int i = 0; i < m; i++){buf[j + i] = a[j + i] + omg[n / l * i] * a[j + i + m];buf[j + i + m] = a[j + i] - omg[n / l * i] * a[j + i + m];}for(int j = 0; j < n; j++)a[j] = buf[j];} }可以預(yù)處理\(\omega_n^k\)和\(\omega_n^{-k}\),分別存在omg和inv數(shù)組中。調(diào)用fft時(shí),如果無(wú)需取倒數(shù),則傳入omg;如果需要取倒數(shù),則傳入inv。
蝴蝶操作
這個(gè)優(yōu)化有著一個(gè)高大上的名字——“蝴蝶操作”。我第一次看到這個(gè)名字時(shí)就嚇跑了——尤其是看到那種帶示意圖的蝴蝶操作解說(shuō)時(shí)。
但是你完全無(wú)需跑!這是一個(gè)很簡(jiǎn)單的優(yōu)化,它可以丟掉上面代碼里的那個(gè)buf數(shù)組。
我們?yōu)槭裁葱枰猙uf數(shù)組?因?yàn)槲覀円鲞@兩件事:
a[j + i] = a[j + i] + omg[n / l * i] * a[j + i + m] a[j + i + m] = a[j + i] - omg[n / l * i] * a[j + i + m]但是我們又要求這兩行不能互相影響,所以我們需要buf數(shù)組。
但是如果我們這樣寫(xiě):
cp t = omg[n / l * i] * a[j + i + m] a[j + i + m] = a[j + i] - t a[j + i] = a[j + i] + t就可以原地進(jìn)行了,不需要buf數(shù)組。
cp a[N], b[N], omg[N], inv[N];void init(){for(int i = 0; i < n; i++){omg[i] = cp(cos(2 * PI * i / n), sin(2 * PI * i / n));inv[i] = conj(omg[i]);} } void fft(cp *a, cp *omg){int lim = 0;while((1 << lim) < n) lim++;for(int i = 0; i < n; i++){int t = 0;for(int j = 0; j < lim; j++)if((i >> j) & 1) t |= (1 << (lim - j - 1));if(i < t) swap(a[i], a[t]); // i < t 的限制使得每對(duì)點(diǎn)只被交換一次(否則交換兩次相當(dāng)于沒(méi)交換)}for(int l = 2; l <= n; l *= 2){int m = l / 2;for(cp *p = a; p != a + n; p += l)for(int i = 0; i < m; i++){cp t = omg[n / l * i] * p[i + m];p[i + m] = p[i] - t;p[i] += t;}} }現(xiàn)在,這個(gè)fft就比之前的遞歸版快很多了!
到此為止我的FFT筆記就整理完啦。
下面貼一個(gè)FFT加速高精度乘法的代碼:
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <complex> #define space putchar(' ') #define enter putchar('\n') using namespace std; typedef long long ll; template <class T> void read(T &x){char c;bool op = 0;while(c = getchar(), c < '0' || c > '9')if(c == '-') op = 1;x = c - '0';while(c = getchar(), c >= '0' && c <= '9')x = x * 10 + c - '0';if(op) x = -x; } template <class T> void write(T x){if(x < 0) putchar('-'), x = -x;if(x >= 10) write(x / 10);putchar('0' + x % 10); } const int N = 1000005; const double PI = acos(-1); typedef complex <double> cp; char sa[N], sb[N]; int n = 1, lena, lenb, res[N]; cp a[N], b[N], omg[N], inv[N]; void init(){for(int i = 0; i < n; i++){omg[i] = cp(cos(2 * PI * i / n), sin(2 * PI * i / n));inv[i] = conj(omg[i]);} } void fft(cp *a, cp *omg){int lim = 0;while((1 << lim) < n) lim++;for(int i = 0; i < n; i++){int t = 0;for(int j = 0; j < lim; j++)if((i >> j) & 1) t |= (1 << (lim - j - 1));if(i < t) swap(a[i], a[t]); // i < t 的限制使得每對(duì)點(diǎn)只被交換一次(否則交換兩次相當(dāng)于沒(méi)交換)}for(int l = 2; l <= n; l *= 2){int m = l / 2;for(cp *p = a; p != a + n; p += l)for(int i = 0; i < m; i++){cp t = omg[n / l * i] * p[i + m];p[i + m] = p[i] - t;p[i] += t;}} } int main(){scanf("%s%s", sa, sb);lena = strlen(sa), lenb = strlen(sb);while(n < lena + lenb) n *= 2;for(int i = 0; i < lena; i++)a[i].real(sa[lena - 1 - i] - '0');for(int i = 0; i < lenb; i++)b[i].real(sb[lenb - 1 - i] - '0');init();fft(a, omg);fft(b, omg);for(int i = 0; i < n; i++)a[i] *= b[i];fft(a, inv);for(int i = 0; i < n; i++){res[i] += floor(a[i].real() / n + 0.5);res[i + 1] += res[i] / 10;res[i] %= 10;}for(int i = res[lena + lenb - 1] ? lena + lenb - 1: lena + lenb - 2; i >= 0; i--)putchar('0' + res[i]);enter;return 0; }轉(zhuǎn)載于:https://www.cnblogs.com/RabbitHu/p/FFT.html
總結(jié)
以上是生活随笔為你收集整理的小学生都能看懂的FFT!!!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一文看懂码灵半导体CFW32C7UL系列
- 下一篇: Highchart series一次只显