FFT详细推导

  • FFT(傅里叶快速变换)
    • 一.前置知识
      • 1.复数和单位根
      • 2.单位根的三个引理
      • 3.多项式
    • 二.FFT(快速傅里叶变换推导)
    • 三.IFFT
    • 四.FFT求解多项式乘积模板代码
      • 1.递归版
      • 2.非递归版(这个更快,省去了递归时间)
    • 五.视频资源
    • 六.FFT题目集

FFT(傅里叶快速变换)

FFT在实际工程中有着非常的广泛,尤其是在信号领域,在ACM算法竞赛领域主要可以用来快速计算多项式的乘积

一.前置知识

1.复数和单位根

有人会觉得FFT怎么会用到复数的知识,想必是有点古怪,后面在推导过程中会介绍到它的用处。

2.单位根的三个引理

3.多项式



前置知识学完了之后下面我们来推导FFT

二.FFT(快速傅里叶变换推导)

DFT的作用是把多项式的系数表示法,转化为点值表示法,复杂度O(n2)O(n^2)O(n2),而FFT则是快速版的DFT,作用是一样的,FFT的复杂度是O(nlogn)O(nlogn)O(nlogn).


上面我们说过n次多项式需要n+1个点来唯一确定,那么我们找点的时候为什么带入的x是复数(也即wi)而不是实数呢?这个视频讲的很详细FFT推导过程。

FFT模板:

/*这里的opt=1*/
void FFT(Complex *a,int lim,int opt)
{if(lim==1) return ;Complex a0[lim>>1],a1[lim>>1];/*我们把多项式的奇数项和偶数项拆开*/ for(int i=0;i<lim;i+=2){a0[i>>1]=a[i],a1[i>>1]=a[i+1];}FFT(a0,lim>>1,opt);//然后递归拆解奇数项 FFT(a1,lim>>1,opt);//递归拆解偶数项 Complex wn = Complex(cos(2.0*PI/lim),opt*sin(2.0*PI/lim));//单位根Complex w = Complex(1,0);//第一个根w0 for(int k=0;k<(lim>>1);k++){Complex t=w*a1[k];//这样会少一次复数运算; a[k]=a0[k]+t;//最后我们把求得的值代回 a[k+(lim>>1)]=a0[k]-t;w=w*wn;//想当于次幂+1 }return ;
}

三.IFFT

我们要解决多项式a多项式b乘积运算,FFT把多项式转化为点值表示之后,我们对两个多项式进行乘积运算,得到一个新的多项式C的点值表示,我们还需要把C的点值表示转化为系数表示,这样才能得到每一项的系数,这时就需要用到FFT的逆过程,IFFT(快速傅里叶逆变换),也就是IDFT(离散傅里叶逆变换)的快速版。


IFFT模板:

/*这里的opt=-1*/
void FFT(Complex *a,int lim,int opt)
{if(lim==1) return ;Complex a0[lim>>1],a1[lim>>1];/*我们把多项式的奇数项和偶数项拆开*/ for(int i=0;i<lim;i+=2){a0[i>>1]=a[i],a1[i>>1]=a[i+1];}FFT(a0,lim>>1,opt);//然后递归拆解奇数项 FFT(a1,lim>>1,opt);//递归拆解偶数项 Complex wn = Complex(cos(2.0*PI/lim),opt*sin(2.0*PI/lim));//单位根Complex w = Complex(1,0);//第一个根w0 for(int k=0;k<(lim>>1);k++){Complex t=w*a1[k];//这样会少一次复数运算; a[k]=a0[k]+t;//最后我们把求得的值代回 a[k+(lim>>1)]=a0[k]-t;w=w*wn;//想当于次幂+1 }return ;
}

通过FFT和IFFT的对比我们发现只有opt参数不一样其余地方全是相同的,FFT的opt=1,IFFT的opt=-1,而opt就只作用于下面这一段代码块。

Complex wn = Complex(cos(2.0*PI/lim),opt*sin(2.0*PI/lim));

这就是我们上面这张图的解释

最后a/n,因为a是一个Complex,所以a/n指的是a的实部除以n,也即a.r/n,因为我们的系数是存在Complex的实部里面的,虚部只是用于FFT和IFFT的递归运算过程。

四.FFT求解多项式乘积模板代码

1.递归版

下面给出整个代码:

#include<bits/stdc++.h>
using namespace std;
const double PI=acos(-1);//3.1415926...
const int Maxn=1e5;
struct Complex
{double r,i;Complex() {r=0.0,i=0.0;}//不带参数的构造函数 Complex(double real,double imag):r(real),i(imag){}//带参数的构造函数
}F[Maxn],G[Maxn];
Complex operator + (Complex a,Complex b){return Complex(a.r+b.r,a.i+b.i);
}
Complex operator - (Complex a,Complex b)
{return Complex(a.r-b.r,a.i-b.i);
}
Complex operator * (Complex a,Complex b)
{return Complex(a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r);
}
/*快速傅里叶变换*/
void FFT(Complex *a,int lim,int opt)
{if(lim==1) return ;Complex a0[lim>>1],a1[lim>>1];/*我们把多项式的奇数项和偶数项拆开*/ for(int i=0;i<lim;i+=2){a0[i>>1]=a[i],a1[i>>1]=a[i+1];}FFT(a0,lim>>1,opt);//然后递归拆解奇数项 FFT(a1,lim>>1,opt);//递归拆解偶数项 Complex wn = Complex(cos(2.0*PI/lim),opt*sin(2.0*PI/lim));//单位根Complex w = Complex(1,0);//第一个根w0 for(int k=0;k<(lim>>1);k++){Complex t=w*a1[k];//这样会少一次复数运算; a[k]=a0[k]+t;//最后我们把求得的值代回 a[k+(lim>>1)]=a0[k]-t;w=w*wn;//想当于次幂+1 }return ;
}
int main()
{int n,m,lim=1;scanf("%d %d",&n,&m);for(int i=0;i<=n;i++) scanf("%lf",&F[i].r);for(int i=0;i<=m;i++) scanf("%lf",&G[i].r);int len = n+m;while(lim<=len) lim<<=1;// FFT(F,lim,1);//我们先通过FFT,把a多项式的系数表示转化为点对表示 FFT(G,lim,1);//我们先通过FFT,也把b多项式的系数表示转化为点对表示/*这时候我们对a,b进行卷积操作,就算F,G没有lim那么多项,但是我们给复数默认传参是0+0i,所以卷积对结果没有影响 */ for(int i=0;i<=lim;i++) F[i]=F[i]*G[i]; FFT(F,lim,-1);//然后我们通过IFFT,也就是快速傅里叶逆变换,把点对表示转化为系数表示 for(int i=0;i<=n+m;i++) printf("%d ",(int)(F[i].r/lim+0.5));//最后的每一个系数/n,然后四舍五入就是答案 }

我们这里测试一下
第一行 我们输入n,m分别是3,3表示多项式a,多项式b的最高次次数。
第二行 我们输入多项式a各项的系数(默认从低次到高次项)
1 2 3 4
第三行 我们输入多项式b各项的系数(默认从低次到高次项)
1 2 3 4

我们的结果是
1 4 10 20 25 24 16

上面想当于解决了多项式A×多项式B的问题,也即A⨁BA\bigoplus BA⨁B
其中A(x)=1+2x+3x2+4x3A(x) = 1+2x+3x^2+4x^3A(x)=1+2x+3x2+4x3,B(x)=1+2x+3x2+4x3B(x) = 1+2x+3x^2+4x^3B(x)=1+2x+3x2+4x3
计算得到C(x)=1+4x+10x2+20x3+25x4+24x5+16x6C(x) = 1+4x+10x^2+20x^3+25x^4+24x^5+16x^6C(x)=1+4x+10x2+20x3+25x4+24x5+16x6

2.非递归版(这个更快,省去了递归时间)

非递归版需要用到蝴蝶效应的知识,本蒟蒻还没有学习完,待更…

五.视频资源

如果看了笔记还是不明白的话,这里推荐一个非常详细的视频讲解,看不懂直接来打我!
建议两个都仔细看一遍,有必要做一下笔记尝试推导
1.FFT的推导细节说明
2.全网最详细FFT,DFT,IDFT,IFFT讲解。

六.FFT题目集

1.模板题,这道题虽然可以之前可以python,现在数据加强了不知道题目建议用FFT,注意高精度进位就可以了,还有就是,数组开到5e7不然会re
P1919 【模板】A*B Problem升级版(FFT快速傅里叶)
AC代码:

#include<bits/stdc++.h>
using namespace std;
const double PI=acos(-1);//3.1415926...
const int Maxn=5e6+5;
struct Complex
{double r,i;Complex() {r=0.0,i=0.0;}//不带参数的构造函数 Complex(double real,double imag):r(real),i(imag){}//带参数的构造函数
}F[Maxn],G[Maxn];
Complex operator + (Complex a,Complex b){return Complex(a.r+b.r,a.i+b.i);
}
Complex operator - (Complex a,Complex b)
{return Complex(a.r-b.r,a.i-b.i);
}
Complex operator * (Complex a,Complex b)
{return Complex(a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r);
}
/*快速傅里叶变换*/
void FFT(Complex *a,int lim,int opt)
{if(lim==1) return ;Complex a0[lim>>1],a1[lim>>1];/*我们把多项式的奇数项和偶数项拆开*/ for(int i=0;i<lim;i+=2){a0[i>>1]=a[i],a1[i>>1]=a[i+1];}FFT(a0,lim>>1,opt);//然后递归拆解奇数项 FFT(a1,lim>>1,opt);//递归拆解偶数项 Complex wn = Complex(cos(2.0*PI/lim),opt*sin(2.0*PI/lim));//单位根Complex w = Complex(1,0);//第一个根w0 for(int k=0;k<(lim>>1);k++){Complex t=w*a1[k];//这样会少一次复数运算; a[k]=a0[k]+t;//最后我们把求得的值代回 a[k+(lim>>1)]=a0[k]-t;w=w*wn;//想当于次幂+1 }return ;
}
char a[Maxn],b[Maxn];
int ans[Maxn];
int main()
{int n=0,m=0,lim=1;scanf("%s",a);scanf("%s",b);n=strlen(a)-1;m=strlen(b)-1;for(int i=n;i>=0;i--) F[n-i].r = a[i]-'0';for(int i=m;i>=0;i--) G[m-i].r = b[i]-'0';int len = n+m;while(lim<=len) lim<<=1;FFT(F,lim,1);//我们先通过FFT,把a多项式的系数表示转化为点对表示 FFT(G,lim,1);//我们先通过FFT,也把b多项式的系数表示转化为点对表示for(int i=0;i<=lim;i++) F[i]=F[i]*G[i]; FFT(F,lim,-1);//然后我们通过IFFT,也就是快速傅里叶逆变换,把点对表示转化为系数表示 for(int i=0;i<=n+m;i++) ans[i]=(int)(F[i].r/lim+0.5);//最后的每一个系数/n,然后四舍五入就是答案 int  num=0;for(int i=0;i<=n+m;i++){ans[i]+=num;num=ans[i]/10;ans[i]%=10;}while(num){ans[++len] = num%10;num/=10;}for(int i=len;i>=0;i--) cout<<ans[i];return 0;
}

FFT(傅里叶快速变换,详细讲解+推导) 每日一遍,算法再见!相关推荐

  1. matlab——FFT傅里叶快速变换

    目录 一.自身的理解与补充 二.其他参考链接 一.转载:https://blog.csdn.net/u013215903/article/details/48091359 FFT是Fast Fouri ...

  2. FFT(傅里叶快速变换算法)计算频率

    傅里叶快速变换 FFT是一个用O(nlog_2 n)的时间将一个用系数表示的多项式转换成它的点值表示的算法,其用于加速多项式高精度乘法的时间O(n^2),是对DFT(离散傅里叶变换)的一个分治的做法. ...

  3. 详细讲解php快速排序,PHP快速排序算法使用步骤详解

    这次给大家带来PHP快速排序算法使用步骤详解,PHP快速排序算法的注意事项有哪些,下面就是实战案例,一起来看一下. 基本思想: 快速排序(Quicksort)是对冒泡排序的一种改进.他的基本思想是:通 ...

  4. 7-14 整数分解为若干项之和 (15分)(附详细讲解(不用递归的高效算法))

    将一个正整数N分解成几个正整数相加,可以有多种分解方法,例如7=6+1,7=5+2,7=5+1+1,-.编程求出正整数N的所有整数分解式子. 输入格式: 每个输入包含一个测试用例,即正整数N (0&l ...

  5. lamport面包店算法详细讲解及代码实现

    lamport面包店算法详细讲解及代码实现 1 算法详解 1.1 一个较为直观的解释 1.2 Lamport算法的时间戳原理 1.3 Lamport算法的5个原则 1.4 一个小栗子 2 算法实现 3 ...

  6. 如何 FFT(快速傅里叶变换) 求幅度、频率(超详细 含推导过程)

    目录 如何 FFT(快速傅里叶变换) 求幅度.频率(超详细 含推导过程) 一. 打颗栗子 二. 求幅度 1. 快速傅里叶变换 2. 求出复数的绝对值 3. 归一化 小结 三. 求频率 1. 频率公式 ...

  7. 超详细易懂FFT(快速傅里叶变换)及代码实现

    前言 昨天学了一晚上,终于搞懂了FFT.希望能写一篇清楚易懂的题解分享给大家,也进一步加深自己的理解. FFT算是数论中比较重要的东西,听起来就很高深的亚子.但其实学会了(哪怕并不能完全理解),会实现 ...

  8. 数字信号处理(DTFT与DFT、DFS的详细讲解以及FFT算法)

    DTFT与DFT.DFS的详细讲解以及FFT算法 DTFT与DFT.DFS的区别在哪里呢? 离散傅里叶级数DFS 离散傅里叶变换DFT 有限长序列的线性卷积和循环卷积 利用DFT做连续信号的频谱分析 ...

  9. 【寒假每日一题】分巧克力(个人练习)详细题解+推导证明(第八天)附带转载程序员壁纸

    文章目录 前言 题目 详细题解 写法1 O(nlogn)O(nlogn)O(nlogn) 推导证明 举一反三 总结 前言 话说今天开始准备搞一个秋招的GitHub,算是复习一遍了. 今天还是寒假每日一 ...

最新文章

  1. 独家 | 手把手教你用Python创建简单的神经网络(附代码)
  2. C++ Opengl 线,网格游戏源码
  3. PostgreSQL的使用-01-创建一个table
  4. 关于javascript的原型和原型链,看我就够了(一)
  5. 如何评估一个ECG分析算法或设备
  6. 构建KEGG pathway、Entrez ID、Ensemble ID的对应关系
  7. Manjaro找不到默认键盘布局
  8. 农村物流配送管理如何走出困境?这里有方案
  9. MemBrain2.0_论文
  10. postman打不开的解决办法
  11. oracle 数据库 date + 1 转载
  12. [SWPU2019]伟大的侦探1
  13. 【算法】1.递归实现二分查找
  14. win10开启远程桌面连接的的一波三折——无法连接到远程计算机、你的凭据不工作
  15. 储能变电站互动系统通讯协议 (征求意见稿)
  16. Mac——开启键盘F1 - F12功能键
  17. 物联网编程语言_编程语言以学习物联网实施
  18. BitTorrent种子文件的解析+(1)
  19. 直播代码开发人员必须懂
  20. 【药监总局】python抓取企业详情数据

热门文章

  1. 计算机考试93781试题及答案,黄南州中小学教师2016年招聘笔试加分人员名单(3 )...
  2. 如何从乱码中恢复 (下)?
  3. c++中transform函数的应用
  4. 什么是MapReduce,MapReduce的工作流程和原理是什么
  5. jkd8 Stream的使用
  6. 计算机中的位,字节,字,字长的概念
  7. MSRN(多尺度超分辨率重建)
  8. wchar* char*相互转换
  9. Java项目:springboot客户关系管理系统
  10. Java中jdk1.8和jdk17相互切换