【清橙A1084】【FFT】快速傅里叶变换

问题描述
  离散傅立叶变换在信号处理中扮演者重要的角色。利用傅立叶变换,可以实现信号在时域和频域之间的转换。
  对于一个给定的长度为n=2 m (m为整数) 的复数序列X 0, X 1, …, X n-1,离散傅立叶变换将得到另一个长度为n的复数序列Y 0, Y 1, …, Y n-1。其中
  Y i=X 0+X 1w i+ X 2w 2i+ X 3w 3i+…+ X n-1w (n-1)i
  其中w=e 2 πI/n=cos(2π/n)+I sin(2π/n),称为旋转因子,其中I为虚数单位,I 2= –1。
  给定输入序列X,请输出傅立叶变换后的输出序列。

  由于所有的复数C都可以表示成C=a+Ib的形式,即由实部和虚部的和表示,所以C可以用一个二元组(a, b)来表示,用这种方法w可表示为(cos(2π/n), sin(2π/n))。复数的计算规则如下:
  (a 1, b 1)+(a 2, b 2)=(a 1+a 2, b 1+b 2)
  (a 1, b 1)(a 2, b 2)=(a 1, b 1)*(a 2, b 2)=(a 1*a 2-b 1*b 2, a 1*b 2+a 2*b 1)

  对于本题,你可以按照上面的公式直接计算,也可以按下面的方法计算。使用上面的公式的复杂度为O(n 2),可以得到一半的分数,而下面的方法的复杂度为O(n log n),可以得到全部的分数。如果要使用上面的公式直接计算,请跳过下面的算法描述部分。
算法描述
  注意到上式中w=e 2 πI/n=cos(2π/n)+I sin(2π/n),所以w n+k=w k,这个公式将在下面的计算用用到。
  对上面的公式进行变形,得到:
  Y i
  =X 0 + X 1w i + X 2w 2i + X 3w 3i +…+ X n-1w (n-1)i
  =X 0 + X 2w 2i + X 4w 4i +…+ X n-2w (n-2)i + w i(X 1 + X 3w 2i + X 5w 4i +…+ X n-1w (n-2)i)
  =(X 0 + X 2φ i + X 4φ 2 i +…+ X n-2φ (n/2-1)i) + w i(X 1 + X 3φ i + X 5φ 2 i +…+ X n-1φ (n/2-1)i)
  其中φ=w 2。利用这个公式可得
  Y i+n/2=(X 0 + X 2φ i+n/2 + X 4φ 2( i+n/2) +…+ X n-2φ (n/2-1) (i+n/2)) + w i(X 1 + X 3φ ( i+n/2) + X 5φ 2( i+n/2) +…+ X n-1φ (n/2-1) (i+n/2))
  其中φ i+n/2=w 2i+n=w 2ii
  所以当i<n/2时,令p i=X 0 + X 2φ i + X 4φ 2 i +…+ X n-2φ (n/2-1)i,q i= X 1 + X 3φ i + X 5φ 2 i +…+ X n-1φ (n/2-1)i,则Y i=p i+w iq i,Y i+n/2= p i+w i+n/2q i
  利用上面的公式,我们可以得到一种快速计算旋转因子为w的傅立叶变换的方法如下(快速傅立叶变换):
  算法Y=Fourier(X, n, w)
  参数:X={X i}为待变换的序列,n为序列的长度(2的整数次幂),w为旋转因子
  结果:X的傅立叶变换Y={Y i}
  1. 计算{X 0, X 2, X 4, …, X n-2}在旋转因子为φ=w 2时的傅立叶变换序列{p i}
  2. 计算{ X 1, X 3, X 5, …, X n-1}在旋转因子为φ=w 2时的傅立叶变换序列{q i}
  3. 对于0<=i<n,计算Y i=p i+w iq i。其中w 0=(1, 0),w i=w i-1*w,你要设置一个临时变量进行累乘而不能每次重新计算w i
  使用这种方法,即可在O(n log n)的时间内计算傅立叶变换。
输入格式
  输入的第一行包含一个整数n,表示待变换的序列的长度,n是2的整数次幂。n<=16384。
  接下来n行,每行2个实数a, b表示序列中的一个元素为(a, b)。
输出格式
  输出n行,每行两个实数,表示经过变换后的序列。为了防止运算时的误差影响结果的输出,请将结果中的每一个实数除以n后保留两位小数。
样例输入
4
1.0 0.0
1.0 0.0
0.0 0.0
0.0 0.0
样例输出
0.50 0.00
0.25 0.25
0.00 0.00
0.25 -0.25
【分析】
也是裸题。
  1 /*

  2 宋代苏轼

  3 《临江仙·夜饮东坡醒复醉》

  4 夜饮东坡醒复醉,归来仿佛三更。家童鼻息已雷鸣。敲门都不应,倚杖听江声。

  5 长恨此身非我有,何时忘却营营。夜阑风静縠纹平。小舟从此逝,江海寄余生。  

  6 */

  7 #include <cstdio>

  8 #include <cstring>

  9 #include <algorithm>

 10 #include <cmath>

 11 #include <queue>

 12 #include <vector>

 13 #include <iostream>

 14 #include <string>

 15 #include <ctime>

 16 #define LOCAL

 17 const double Pi = acos(-1.0);

 18 const int MAXN = 200000 + 10;

 19 using namespace std;

 20 typedef long long ll;

 21 struct Num {

 22    double a , b;

 23     //构造函数

 24    Num(double x = 0,double y = 0) {a = x; b = y;}

 25     //复数的三种运算

 26    Num operator + (const Num &c) {return Num(a + c.a, b + c.b);}

 27    Num operator - (const Num &c) {return Num(a - c.a, b - c.b);}

 28    Num operator * (const Num &c) {return Num(a * c.a - b * c.b, a * c.b + b * c.a);}

 29 }x1[MAXN] , x2[MAXN];

 30 

 31 //注意loglen为log后的长度

 32 void change(Num *t, int len, int loglen){

 33     //蝶形变换后的序列编号

 34     for (int i = 0; i < len; i++){

 35         int x = i, k = 0;

 36         for (int j = 0; j < loglen; j++, x >>= 1) k = (k << 1) | (x & 1);

 37         if (k < i) swap(t[k], t[i]);

 38     }

 39 }

 40 //基2-FFT

 41 void FFT(Num *x, int len, int loglen){

 42     change(x, len, loglen);

 43     int t = 1;

 44     //t为长度

 45     for (int i = 0; i < loglen; i++, (t <<= 1)){

 46         int l = 0, r = l + t;

 47         while (l < len){

 48             //初始化

 49             Num a, b, wo(cos(Pi / t), sin(Pi / t)), wn(1, 0);

 50             for (int j = l; j < l + t; j++){

 51                 a = x[j];

 52                 b = x[j + t] * wn;

 53                 //蝶形计算

 54                 x[j] = a + b;

 55                 x[j + t] = a - b;

 56                 wn = wn * wo;

 57             }

 58             //注意是合并所以要走2t步

 59             l = r + t;

 60             r = l + t;

 61         }

 62     }

 63 }

 64 //离散傅里叶变换

 65 void DFT(Num *x, int len, int loglen){

 66     int t = (1<<loglen);

 67     for (int i = 0; i < loglen; i++){

 68         t >>= 1;

 69         int l = 0, r = l + t;

 70         while (l < len){

 71             Num a, b, wn(1, 0), wo(cos(Pi / t), -sin(Pi / t));

 72             for (int j = l; j < l + t; j++){

 73                 a = x[j] + x[j + t];

 74                 b = (x[j] - x[j + t]) * wn;

 75                 x[j] = a;

 76                 x[j + t] = b;

 77                 wn = wn * wo;

 78             }

 79             l = r + t;

 80             r = l + t;

 81         }    

 82     }

 83     change(x, len, loglen);

 84     for (int i= 0; i < len; i++) x[i].a /= len;

 85 }

 86 int solve(char *a, char *b){

 87     int len1, len2, len, loglen;

 88     int t, over;

 89     len1 = strlen(a) << 1;

 90     len2 = strlen(b) << 1;

 91     len = 1;

 92     loglen = 0;

 93     while (len < len1) len <<= 1, loglen++;

 94     while (len < len2) len <<= 1, loglen++;

 95     //处理len1

 96     for (int i = 0; i < len; i++){

 97         if (a[i]) x1[i].a = a[i] - '0', x1[i].b = 0;

 98         else x1[i].a = x1[i].b = 0;

 99     }

100     for (int i = 0; i < len; i++){

101         if (b[i]) x2[i].a = b[i] - '0', x2[i].b = 0;

102         else x2[i].a = x2[i].b = 0;

103     }

104     FFT(x1, len, loglen);

105     FFT(x2, len, loglen);

106     for (int i = 0; i < len; i++) x1[i] = x1[i] * x2[i];

107 

108     DFT(x1, len, loglen);

109     over = len = 0;

110     //转换成十进制的整数

111     for (int i = ((len1 + len2) / 2) - 2; i >= 0; i--){

112         t = (int)((double)x1[i].a + (double)over + 0.5);

113         a[len++] = t % 10;

114         over = t / 10;

115     }

116     while (over){

117         a[len++] = over % 10;

118         over /= 10;

119     }

120     return len;

121 }

122 //输出

123 void print(char *str, int len){

124      for(len--; len>=0 && !str[len];len--);

125     if(len < 0) putchar('0');

126     else for(;len>=0;len--) putchar(str[len]+'0');

127     printf("\n");

128 }

129 char a[MAXN] , b[MAXN];

130 

131 int main() {

132     int n;

133     

134     scanf("%d", &n);

135     for (int i = 0; i < n; i++) scanf("%lf%lf", &x1[i].a, &x1[i].b);

136     int t = 0;

137     while ((1<<t) < n) t++;

138     FFT(x1, n, t);

139     for (int i = 0; i < n; i++) printf("%.2lf %.2lf\n", x1[i].a / n, x1[i].b / n);

140    return 0;

141 }
View Code

 

你可能感兴趣的:(快速)