FFT习题
Posted 黑大帅之家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFT习题相关的知识,希望对你有一定的参考价值。
Q1(hdu1402):
给出两个很大的数字A,B,计算二者乘积。
分析:这个题目java应该能过,用FFT做能够加速计算。这里将字符串A按权(10进制)展开,前面的系数就是多项式的系数,这样就构造出了多项式乘积形式,然后用FFT加速即可。
参考代码如下:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <math.h> using namespace std; const double PI = acos(-1.0); //复数结构体 struct complex { double r,i; complex(double _r = 0.0,double _i = 0.0) { r = _r; i = _i; } complex operator +(const complex &b) { return complex(r+b.r,i+b.i); } complex operator -(const complex &b) { return complex(r-b.r,i-b.i); } complex operator *(const complex &b) { return complex(r*b.r-i*b.i,r*b.i+i*b.r); } }; /* * 进行FFT和IFFT前的反转变换。 * 位置i和 (i二进制反转后位置)互换 * len必须去2的幂 */ void change(complex y[],int len) { int i,j,k; for(i = 1, j = len/2;i < len-1; i++) { if(i < j)swap(y[i],y[j]); //交换互为小标反转的元素,i<j保证交换一次 //i做正常的+1,j左反转类型的+1,始终保持i和j是反转的 k = len/2; while( j >= k) { j -= k; k /= 2; } if(j < k) j += k; } } /* * 做FFT * len必须为2^k形式, * on==1时是DFT,on==-1时是IDFT */ void fft(complex y[],int len,int on) { change(y,len); for(int h = 2; h <= len; h <<= 1) { complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h)); for(int j = 0;j < len;j+=h) { complex w(1,0); for(int k = j;k < j+h/2;k++) { complex u = y[k]; complex t = w*y[k+h/2]; y[k] = u+t; y[k+h/2] = u-t; w = w*wn; } } } if(on == -1) for(int i = 0;i < len;i++) y[i].r /= len; } const int MAXN = 200010; complex x1[MAXN],x2[MAXN]; char str1[MAXN/2],str2[MAXN/2]; int sum[MAXN]; int main() { while(scanf("%s%s",str1,str2)==2) { int len1 = strlen(str1); int len2 = strlen(str2); int len = 1; while(len < len1*2 || len < len2*2)len<<=1; for(int i = 0;i < len1;i++) x1[i] = complex(str1[len1-1-i]-\'0\',0); for(int i = len1;i < len;i++) x1[i] = complex(0,0); for(int i = 0;i < len2;i++) x2[i] = complex(str2[len2-1-i]-\'0\',0); for(int i = len2;i < len;i++) x2[i] = complex(0,0); //求DFT fft(x1,len,1); fft(x2,len,1); for(int i = 0;i < len;i++) x1[i] = x1[i]*x2[i]; fft(x1,len,-1); for(int i = 0;i < len;i++) sum[i] = (int)(x1[i].r+0.5); for(int i = 0;i < len;i++) { sum[i+1]+=sum[i]/10; sum[i]%=10; } len = len1+len2-1; while(sum[len] <= 0 && len > 0)len--; for(int i = len;i >= 0;i--) printf("%c",sum[i]+\'0\'); printf("\\n"); } return 0; }
Q2(hdu4609):
给出一个n元素整数集合,问任意取出三个元素,组成三角形的概率。
分析:这里只需要计数整数集合中能够组成多少个三角形即可,然后除以C(n , 3).对于如何计数能够组成多少个三角形,纯暴力时间上是O(n^3),需要优化。
参考代码如下:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <math.h> using namespace std; const double PI = acos(-1.0); struct Complex { double r,i; Complex(double _r = 0,double _i = 0) { r = _r; i = _i; } Complex operator +(const Complex &b) { return Complex(r+b.r,i+b.i); } Complex operator -(const Complex &b) { return Complex(r-b.r,i-b.i); } Complex operator *(const Complex &b) { return Complex(r*b.r-i*b.i,r*b.i+i*b.r); } }; void change(Complex y[],int len) { int i,j,k; for(i = 1, j = len/2;i < len-1;i++) { if(i < j)swap(y[i],y[j]); k = len/2; while( j >= k) { j -= k; k /= 2; } if(j < k)j += k; } } void fft(Complex y[],int len,int on) { change(y,len); for(int h = 2;h <= len;h <<= 1) { Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h)); for(int j = 0;j < len;j += h) { Complex w(1,0); for(int k = j;k < j+h/2;k++) { Complex u = y[k]; Complex t = w*y[k+h/2]; y[k] = u+t; y[k+h/2] = u-t; w = w*wn; } } } if(on == -1) for(int i = 0;i < len;i++) y[i].r /= len; } const int MAXN = 400040; Complex x1[MAXN]; int a[MAXN/4]; long long num[MAXN];//100000*100000会超int long long sum[MAXN]; int main() { int T; int n; scanf("%d",&T); while(T--){ scanf("%d",&n); memset(num,0,sizeof(num)); for(int i = 0;i < n;i++){ scanf("%d",&a[i]); num[a[i]]++; } sort(a,a+n); int len1 = a[n-1]+1; int len = 1; while( len < 2*len1 )len <<= 1; for(int i = 0;i < len1;i++) x1[i] = Complex(num[i],0); for(int i = len1;i < len;i++) x1[i] = Complex(0,0); fft(x1,len,1); for(int i = 0;i < len;i++) x1[i] = x1[i]*x1[i]; fft(x1,len,-1); for(int i = 0;i < len;i++) num[i] = (long long)(x1[i].r+0.5); len = 2*a[n-1];//为105行代码的处理优化一下界 for(int i = 0;i < n;i++) {num[a[i]+a[i]]--;} //减掉取两个相同的组合 for(int i = 1;i <= len;i++) {num[i]/=2;} //选择的无序,除以2 sum[0] = 0; for(int i = 1;i <= len;i++){sum[i] = sum[i-1]+num[i];}//卷积的前缀和 long long cnt = 0;//能够组成三角形的个数 for(int i = 0;i < n;i++){ cnt += sum[len]-sum[a[i]]; cnt -= (long long)(n-1-i)*i; //减掉一个取大,一个取小的 cnt -= (n-1); //减掉一个取本身,另外一个取其它 cnt -= (long long)(n-1-i)*(n-i-2)/2;//减掉大于它的取两个的组合 } //总数 long long tot = (long long)n*(n-1)*(n-2)/6;//答案概率的分母C(n , 3) printf("%.7lf\\n",(double)cnt/tot); } return 0; }
Q3(hdu5885):
给出一个n*m的格点,每个格点有一个权值。给定一个半径r,对于某个点(x,y),他周围到他的欧几里得距离小于r的格点,都会对(x,y)产生一个贡献度。那么问在这个n*m格点图中,获得最大贡献度的格点的贡献度是多少。
参考代码如下:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <math.h> using namespace std; const double PI = acos(-1.0); struct Complex { double r,i; Complex(double _r = 0,double _i = 0) { r = _r; i = _i; } Complex operator +(const Complex &b) { return Complex(r+b.r,i+b.i); } Complex operator -(const Complex &b) { return Complex(r-b.r,i-b.i); } Complex operator *(const Complex &b) { return Complex(r*b.r-i*b.i,r*b.i+i*b.r); } }; void change(Complex y[],int len) { int i,j,k; for(i = 1, j = len/2;i < len-1;i++) { if(i < j)swap(y[i],y[j]); k = len/2; while( j >= k) { j -= k; k /= 2; } if(j < k)j += k; } } void fft(Complex y[],int len,int on) { change(y,len); for(int h = 2;h <= len;h <<= 1) { Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h)); for(int j = 0;j < len;j += h) { Complex w(1,0); for(int k = j;k < j+h/2;k++) { Complex u = y[k]; Complex t = w*y[k+h/2]; y[k] = u+t; y[k+h/2] = u-t; w = w*wn; } } } if(on == -1) for(int i = 0;i < len;i++) y[i].r /= len; } const int MAXN = (1 << 21); Complex x1[MAXN] , x2[MAXN]; //一开始两个多项式的系数向量,经过fft之后记录多项式在单位复数根处的值 double dis(int x , int y){ return sqrt(x*x + y*y); } int main() { int n , m , R; double r; while(~scanf("%d%d%lf" , &n , &m , &r)){ R = ceil(r); int M = m + 2 * R; int len = 1;//len是两个多项式的界 while( len < M * M )len <<= 1; for(int i = 0;i < len;++i) x1[i] = x2[i] = Complex(0 , 0);//初始化一下 //得到第一个多项式的系数向量 for(int i = 0;i < n;i++){ for(int j = 0;j < n;j++){ int p;scanf("%d" , &p); x1[i * M + j] = Complex(p , 0); } } //得到第二个多项式的系数向量 for(int i = -R;i <= R;++i){ for(int j = -R;j <= R;++j){ if(dis(i , j) < r) x2[(i+R)*M + j + R] = Complex(1.0/(dis(i , j) + 1), 0); } } fft(x1 , len , 1); fft(x2 , len , 1); //得到两个的多项式在单位复数根处的值,用x1[],x2[]存储 for(int i = 0;i < len;i++) x1[i] = x1[i] * x2[i]; //得到多项式乘法的点值表达 fft(x1 , len , -1);//逆DFT将点值表达转化成系数表达 double ans = 0; for(int i = 0;i < n;++i) for(int j = 0;j < m;++j) ans = max(ans , x1[(i+R)*M + (j + R)].r); printf("%.3lf\\n" , ans); } return 0; }
以上是关于FFT习题的主要内容,如果未能解决你的问题,请参考以下文章
C语言习题如何在 C 中不使用任何分号打印从 1 到 N 的数字?