FFT学习笔记

前言

本文主要是谈一谈自己对蝴蝶变换以及非递归FFT的理解,如果想全面学习FFT算法,请移步其他博客。

蝴蝶变换

为什么要引进蝴蝶变换?

FFT的递归过程实际上是拆分奇数次和偶数次项的过程,每次将偶数次项移到左边,奇数次项移到右边,拆分成两个子多项式,以便递归计算。
为了消除递归中,奇偶次项移动交换产生的冗余时间消耗,引入蝴蝶变换,在FFT之前将每一项排好顺序,递归(迭代)的时候就直接二分变换后的数组就行了。

具体操作?

蝴蝶变换,即将一个数在二进制表示下翻转整个二进制串形成的数。
考虑从小到大计算 i i i翻转后的数 R i R_i Ri
对于 i i i,我们先让 i i i右移一位,然后翻转(即 R i / 2 R_{i/2} Ri/2),然后再右移一位,可以写几个数模拟一下,发现此时只有最低位还未翻转,判断一下即可。
简化版公式 R i = R i / 2 / 2 + ( i m o d 2 ) ∗ n / 2 R_i=R_{i/2}/2+(i mod 2)*n/2 Ri=Ri/2/2+(imod2)n/2其中 n n n为数组长度(必为2的幂次)。

非递归FFT

递归FFT中需要用到三个数组,分别表示原多项式的 n n n个取值,子多项式的 n / 2 n/2 n/2个取值。
但是非递归FFT中只需用到一个数组即可。
在蝴蝶变换的帮助下,非递归FFT从最底层开始依次向上迭代,即先计算长度为1的子多项式的1个点的取值(也就是多项书的系数),再计算长度为2的子多项式的2个点的取值,依次类推,直至长度为 n n n的原多项式的 n n n个点的取值。
譬如:
假设FFT中用到的数组为 y y y,第一层(最底层)时, y i y_i yi代表子多项式第 i i i项取 w 1 1 w_1^1 w11的情况,第二层时, y i y_i yi, y i + 1 y_{i+1} yi+1代表子多项式第 i , i + 1 i,i+1 i,i+1项取 w 2 1 w_2^1 w21 w 2 2 w_2^2 w22的情况,第三层时, y i y_{i} yi y i + 1 y_{i+1} yi+1 y i + 2 y_{i+2} yi+2 y i + 3 y_{i+3} yi+3代表子多项式第 i , i + 1 , i + 2 , i + 3 i,i+1,i+2,i+3 i,i+1,i+2,i+3项取 w 4 1 w_4^1 w41 w 4 2 w_4^2 w42 w 4 3 w_4^3 w43 w 4 4 w_4^4 w44的情况,以此类推。
每一层都会从左到右更新一遍y数组的值

合并时:(以第三层为例),子多项式第 i , i + 1 , i + 2 , i + 3 i,i+1,i+2,i+3 i,i+1,i+2,i+3项取 w 4 1 w_4^1 w41 w 4 2 w_4^2 w42的值 f ( w 4 1 ) f(w_4^1) f(w41) f ( w 4 2 ) f(w_4^2) f(w42)
由底下的子多项式第 i , i + 1 i,i+1 i,i+1项的 f ( w 2 1 ) f(w_2^1) f(w21) f ( w 2 2 ) f(w_2^2) f(w22)的值
加上 w 4 1 w_4^1 w41乘上子多项式第 i + 2 , i + 3 i+2,i+3 i+2,i+3项的 f ( w 2 1 ) f(w_2^1) f(w21) f ( w 2 2 ) f(w_2^2) f(w22)的值计算到;
子多项式第 i , i + 1 , i + 2 , i + 3 i,i+1,i+2,i+3 i,i+1,i+2,i+3项取 w 4 3 w_4^3 w43 w 4 4 w_4^4 w44的值 f ( w 4 3 ) f(w_4^3) f(w43) f ( w 4 4 ) f(w_4^4) f(w44)
由底下的子多项式第 i , i + 1 i,i+1 i,i+1项的 f ( w 2 1 ) f(w_2^1) f(w21) f ( w 2 2 ) f(w_2^2) f(w22)的值
减去 w 4 1 w_4^1 w41乘上子多项式第 i + 2 , i + 3 i+2,i+3 i+2,i+3项的 f ( w 2 1 ) f(w_2^1) f(w21) f ( w 2 2 ) f(w_2^2) f(w22)的值计算到.

代码

#define ld double
ld PI=acos(-1.0);
for(int i=0;i<len;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)); //蝴蝶变换(main中)

void bft(complex<ld> y[]){	//	每次fft之前
    for(int i=0;i<len;i++){
        if(i<r[i]){
            swap(y[i],y[r[i]]);
        }
    }
    return;
}
void fft(complex<ld> y[],int zt){	//zt判断正还是逆变换
    bft(y);
    for(int i=2;i<=len;i<<=1){
        complex<ld> step(cos(2.0*PI/i),sin(2.0*PI/i*zt));
        for(int j=0;j<len;j+=i){
            complex<ld> w(1,0);
            for(int k=j;k<j+i/2;k++){
                complex<ld> temp1=y[k];
                complex<ld> temp2=w*y[k+i/2];
                y[k]=temp1+temp2;
                y[k+i/2]=temp1-temp2;
                w=w*step;
            }
        }
    }
    if(zt==-1){
        for(int i=0;i<len;i++){
            ld temp=y[i].real();
            temp/=len;
            y[i].real(temp);
        }
    }
    return;
}

后记

FFT可以对应实数的四则运算中的乘法,后面所有的运算操作(除法,取模,取对数)都是要在FFT的基础上进行,所以就像学小学数学必须学好加减乘除一样,学多项式也就必须学好FFT,必须弄懂所有细节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值