FFT(快速傅里葉變換)是上個學期學會的東西,由于接下來要玩母函數,所以現在寫篇博客復習一下。FFT可以在
給定兩個十進制數(
不妨把它歸為一個多項式乘積的問題:每一位都是一個系數,那么1234*2333就變成了:
對于多項式乘積問題,顯然跑
因此,我們做的事情是直接計算答案的每一位。現在我們換一種思路。
之前我們使用的系數表達。因為它給出了系數向量:
但是考慮這樣一個事實:給定
因此我們擁有了一種全新的表達多項式的方式:點值表達。給出
有什么用呢? 考慮多項式的加法
則
看圖很好理解:
我們把從點值表達變成系數表達的過程乘坐插值。
那么多項式的乘法也類似。大概是這樣的步驟:
對著數學書腦補一番就解決了。
單位復數根:
看圖應該能腦補出來:盡管
在圖中的意義是:第一象限點的平方與第三象限點的平方一致;第二象限點的平方與第四象限點的平方一致。因為
由于這貨的性質,我們選擇單位復數根作為
關鍵步驟:將
由于我們使用單位復數根進行求值,則
那么如何插值回來呢?令
上面那段話并不清楚,因為我太弱了,根本講不清楚。要完全理解上面的話,推薦看完代碼,然后啃一啃《導論》。
所以我們就寫出了代碼:uoj #34.多項式乘法
#include <cstdio>#include <cstdlib>#include <cmath>#include <complex>#include <iostream>using namespace std;typedef complex<double> cp; //complex庫void fft(cp *a,int n,int flag) //作用:求出a的點值表達,存進a{ int i; cp a0[n/2+1],a1[n/2+1]; if(n==1) return; cp w_n(cos(2*M_PI/n),sin(flag*2*M_PI/n)); //flag=1:求值 flag=2:插值 cp w(1,0); for(i=0;i<n/2;i++) a0[i]=a[i*2],a1[i]=a[i*2+1]; //分治 fft(a0,n/2,flag); fft(a1,n/2,flag); for(i=0;i<n/2;i++) { a[i]=a0[i]+w*a1[i]; a[i+n/2]=a0[i]-w*a1[i]; w=w*w_n; //遞推單位復數根 }}cp x[300005]={0},y[300005]={0};int n,m;void init(){ int i; scanf("%d%d",&n,&m); for(i=0;i<=n;i++) cin>>x[i].real(); for(i=0;i<=m;i++) cin>>y[i].real(); m+=n; for(n=1;n<=m;n=n*2);}int main(void){ int i; init(); fft(x,n,1); //求值 fft(y,n,1); //求值 for(i=0;i<n;i++) x[i]=x[i]*y[i]; //點值乘法 fft(x,n,-1); //插值 for(i=0;i<=m;i++) 跑了4460ms。提交記錄 遞歸版的比較慢(廢話),迭代版的跑得比香港記者還快。 然而我并不會蝴蝶操作那套理論,請看riteme的博客:有關多項式的算法
相關資料
riteme 快速數論變換(NTT) 簡要介紹了快速數論變換。xlightgod UOJ34 多項式乘法適合入門FFT。我就是在那里學習了一個。iamzky 快速傅里葉變換講解很詳細。
新聞熱點
疑難解答