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

2023-05-16

FFT详细推导

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

FFT(傅里叶快速变换)

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

一.前置知识

1.复数和单位根

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

2.单位根的三个引理

在这里插入图片描述

3.多项式

在这里插入图片描述
在这里插入图片描述
前置知识学完了之后下面我们来推导FFT

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

DFT的作用是把多项式的系数表示法,转化为点值表示法,复杂度 O ( n 2 ) O(n^2) O(n2),而FFT则是快速版的DFT,作用是一样的,FFT的复杂度是 O ( n l o g n ) 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 ⨁ B A\bigoplus B AB
其中 A ( x ) = 1 + 2 x + 3 x 2 + 4 x 3 A(x) = 1+2x+3x^2+4x^3 A(x)=1+2x+3x2+4x3, B ( x ) = 1 + 2 x + 3 x 2 + 4 x 3 B(x) = 1+2x+3x^2+4x^3 B(x)=1+2x+3x2+4x3
计算得到 C ( x ) = 1 + 4 x + 10 x 2 + 20 x 3 + 25 x 4 + 24 x 5 + 16 x 6 C(x) = 1+4x+10x^2+20x^3+25x^4+24x^5+16x^6 C(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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • Ubuntu下Qt程序生成Core文件便于调试

    需要在运行时生成core dump文件 xff0c 以排查出错的代码行 首先在pro结尾里加入 xff1a QMAKE CC 43 61 g QMAKE CXX 43 61 g QMAKE LINK 43 61 g 在终端输入 ulimit
  • linux查看进程所有子进程和线程

    线程是现代操作系统上进行并行执行的一个流行的编程方面的抽象概念 当一个程序内有多个线程被叉分出用以执行多个流时 xff0c 这些线程就会在它们之间共享特定的资源 xff08 如 xff0c 内存地址空间 打开的文件 xff09 xff0c
  • 一款二进制文件查看器

    由于使用的是Notepad 43 43 64位版本 xff0c 在网上找了很多二进制查看插件HexEditor dll要么是32位不兼容 xff0c 要么是出现除零的错误 xff08 以前找到过一次支持Notepad 43 43 64位版本
  • YOLO目标检测

    一 背景 基于深度学习技术的视觉目标检测近年去的长足发展 但仍然有许多方面问题需要优化 二 YOLO算法的特点 YOLO作为一种性能优异的通用目标检测系统 xff0c 为了保证检测的效率 xff0c 提出one stage的思想 xff0c
  • (Qt中添加编译选项)QT在交叉编译时出现parameter passing for argument of type ‘std::_Rb_tree xxxxx changed in GCC 7.1

    QT版本都是5 1x 先是在Ubuntu机器上写的代码 xff0c GCC版本为5 4 xff0c 代码编译无 任何警告 后来移植到开发板 xff08 GCC版本为7 1 xff09 进行编译时 xff0c 提示这种警告 发生在代码中对st
  • C++按行读取文本并解析

    项目中需要按行读取文本文件 xff0c 并对每一行内容进行解析 每一行都是固定的字段数 xff0c 字段之间用空格隔开 span class token macro property span class token directive k
  • error C2447: “{”: 缺少函数标题(是否是老式的形式表?)

    error C2447 缺少函数标题 是否是老式的形式表 网上有人说 这个BUG是因为在win7上使用了 LF 的格式编码导致的 使用Notepad 43 43 修改成 BOM UTF8 和 windows 的 CR LF 格式一切正常 确
  • Visual Studio 2017 代码自动对齐

    点 编辑 高级 设置选定内容的格式 或者按Ctrl 43 K 然后再按Ctrl 43 F 就好了 你可以在常用快捷键自定义 窗口中进行查看 1 进入工具 选项 对话框 2 选择 环境 键盘 3 在 显示命令包含 下面的对话框中输入 对齐 关
  • CSDN 排版之颜色、字体、字号及背景色

    颜色 xff1a span class token operator lt span font color span class token operator 61 span blue span class token operator g
  • 使用QTCreator编程时,如何利用dmp文件定位程序奔溃

    写这篇文章之前 xff0c 看了一些其他人的博客 xff0c 但不是很详细 xff0c 缺这少那的 xff0c 好多都是复制粘贴别人的东西 自己动手弄了弄 xff0c 可以使用 xff0c 就记下来备忘与分享 前言 开发环境说明 编程IDE
  • Pytorch中transforms.RandomResizedCrop使用说明

    加载数据 训练集数据扩充 数据增强 和归一化 数据扩充 数据增强 的意义在于通过人为的随机范围裁剪 缩放 旋转等操作 增大训练集中的数据多样性 全面性 进而提高模型的泛化能力 训练集数据扩充和归一化 在验证集上仅需要归一化 data tra
  • C++中调用Python的办法

    1 背景 一直采用C 43 43 作为主语言开发 xff0c 最近遇到一个项目需要解析PDF文件中的文本内容 xff0c 直接采用C 43 43 来做显得不是很方便 xff0c 但用python来做就显得很简单了 难点在于如何C 43 43
  • Qt Creator远程调试嵌入式ARM开发板

    1 环境 Win10 64位系统上通过Virtual Box安装了一个Ubuntu虚拟机 ubuntu的版本 xff1a Linux kernel 4 15 0 142 generic 146 16 04 1 Ubuntu SMP Ubun
  • 套接字(描述符)读取指定的字节数

    检测fd句柄是否可读 xff0c ms毫秒超时 参数 xff1a df in 检测的句柄 ms in 超时 xff0c 毫秒 返回 xff1a 1 可读 xff0c 或者已经断开 0 超时 xff0c 仍然不可读 1 错误 int IsRe
  • 4.4线索二叉树遍历

    1 中序线索二叉树遍历 找到第一个中序遍历的结点 ThreadNode span class token operator span span class token function Firstnode span span class t
  • 自动根据本机字节序 将小端字节序的报文(字符数组)转为整数

    1 xff0c 判断本机的字节序 xff08 大端优先 小端优先 xff09 判断当前PC为大端还是小端字节序 64 返回值 xff1a 1 大端 xff1b 0 小端 int JudgeEndianOfPC int num 61 1 if
  • 智能指针的使用

    智能指针在C 43 43 11版本之后提供 xff0c 包含在头文件 lt memory gt 中 xff0c shared ptr unique ptr weak ptr 1 xff0c shared ptr的使用 shared ptr使
  • 图像检索系列——利用深度学习实现以图搜图

    转载自 xff1a 图像检索系列 利用深度学习实现以图搜图 知乎 前言 在上一篇文章 图像检索系列 利用 Python 检测图像相似度 中 xff0c 我们介绍了一个在图像检索领域非常常用的算法 感知哈希算法 这是一个很简单且快速的算法 x
  • Windows下select模型(以及EAGAIN、EWOULDBLOCK、EINTR)

    在这里记录一下 xff0c 以前都是新项目用到了就从旧项目中拷贝 自从将博客当作记事本 xff0c 发现自己多了一个好习惯 Windows下select模型 程序员攻略 CSDN博客 套接字IO超时设置和使用select实现超时管理 wj
  • RANSAC算法理解

    RANSAC是 RANdom SAmple Consensus xff08 随机抽样一致 xff09 的缩写 它可以从一组包含 局外点 的观测数据集中 xff0c 通过迭代方式估计数学模型的参数 它是一种不确定的算法 它有一定的概率得出一个

随机推荐

  • C++ 环形缓冲区(队列)简单实现

    1 说明 在实际工作中 xff0c 如果数据流量过大 xff0c 可以先把数据接收到数据缓冲区中 xff0c 处理之后再取出 我们定义的包协议可以采用定长包 xff0c 可以采用不定长度的包 xff0c 环形缓冲区都能处理 2 使用场景 2
  • Visual Studio Code (vscode) 配置 C / C++ 环境

    Visual Studio Code vscode 配置 C C 43 43 环境 步平凡 博客园 在电脑安装软件管控严格的情况下 xff0c 想装VS装不了 xff0c 就装轻量版的VSCode了 以上写得很好 xff0c 照做即可 本人
  • c++实现basename

    window API居然不包含Linux中很好用的basename函数 xff0c 实现了一下 xff0c 留个记录 xff0c 省得日后重复写 std string m basename std string fullPath size
  • tortoiseGit教程

    0 前言 TortoiseGit其实是一款开源的git的版本控制系统 xff0c 也叫海龟git TortoiseGit提供了人性化的图形化界面 xff0c 不用像Git一样输入许多语句 xff0c 像git init git add gi
  • 用STL库创建线程

    测试了3种方式 xff1a 1 xff1a 子线程不带返回值 2 xff1a 子线程带返回值 3 xff1a 子线程带引用类型参数 使用join方式 xff0c 让父线程等待子线程运行结束 TestTemp cpp 定义控制台应用程序的入口
  • 4.5树的存储

    双亲表示法 xff0c 孩子表示法 xff0c 孩子兄弟表示法 1 双亲表示法 查找双亲简单 空数据导致遍历更慢 xff0c 查指定节点的孩子只能遍历 span class token keyword typedef span ElemTy
  • Windows下MySQL数据库的安装、配置及C++使用案例

    1 安装及配置 Windows判断本地是否安装mysql以及mysql安装过程 企鹅要去银河思考人生 xff01 xff01 xff01 的博客 CSDN博客 windows查看是否安装mysql 注意按照文中提示 xff0c 配置好环境变
  • C++获取系统毫秒级时间(自1970年1月1日至今的毫秒数)

    跟系统时间相关的 ifdef WIN32 include lt time h gt include lt windows h gt else include lt sys time h gt endif unsigned long long
  • Window 10下SQL Server的安装配置以及C++使用案例

    1 SQL Server2008的安装与配置 参照下面这篇博客实现即可 里面提供了安装包下载方式 xff08 百度网盘有点慢 xff09 安装及配置步骤 SQLServer安装教程 xff08 史上最详细版本 xff09 杨林伟的博客 CS
  • 基于OpenCV实现的多角度、多目标模板匹配项目实战案例

    1 说明 本案例采用NCC的匹配 金字塔 为了加速 思想 基于OpenCV实现的多角度 多目标模板匹配 不支持尺度不变 若研究旋转 尺度不变性的匹配 请参考本人的OpenCV专栏内的 nbsp OpenCV实现多角度多尺度模板匹配 基于形状
  • 程序日志模块的两种模式

    程序员都知道程序的运行日志在不少时候都非常有用 xff0c 利于排查 理清逻辑 一般而言 xff0c 日志都按天生成 xff0c 并且具备自动清理多少天以前的旧日志 xff0c 避免无限增长占用磁盘 下图展示了2种日志模式 模式一 1 xf
  • C++开发面试常考

    C 43 43 后台开发面试常考 pudn com
  • 如何在微软官网上下载旧版本的visual studio

    想在微软官网下载旧版本的VS 太长不想看的可以直接戳网址进入最终的界面 xff1a Visual Studio 较旧的下载 2019 2017 2015 和以前的版本想从官网首页一步一步进入到最终下载界面的可以看下面详细步骤 xff1a 1
  • 基于OpenCV实现的RANSAC随机抽样一致性直线拟合

    概要 本文介绍基于ransac随机抽样一致性随机抽样一致性的直线拟合方法 涵盖一下的内容 ransac的算法思想 ransac的算法步骤 如何调整ransac算法迭代的次数 基于opencv编码实现 ransac算法流程 RANSACRAN
  • 基于OpenCV(C++)实现的RANSAC随机抽样一致性的曲线拟合(二次)

    nbsp 0 前言 nbsp nbsp 前不久整理了RANSAC直线拟合的文章 基于OpenCV实现的RANSAC随机抽样一致性直线拟合 thequitesunshine007的博客 CSDN博客 这篇文章与其类似 只是从拟合直线变为拟合曲
  • 最小二乘least-squares拟合曲线(三次或多次)

    1 说明 基于最小least squares去拟合出多次曲线 考虑到了所有的样本点 因此这种方法对噪声敏感 尤其是遇到较为突兀明显的噪声时 曲线的形状易受干扰 2 代码 代码细节仔细读基本都能读懂 或者查一下也不是什么大问题 include
  • 4.6树和森林的遍历

    一 树的遍历 1 先根遍历 对应二叉树先序遍历 span class token keyword void span span class token function PreOrder span span class token punc
  • 扩展欧几里得算法(简单易懂,详细分析)

    扩展欧几里得算法 扩展欧几里得算法证明 43 应用 61 61 欧几里得算法 61 61 61 61 扩展欧几里得算法 61 61 应用1 xff1a 求一元二次线性方程的整数解应用二 xff1a 求ax 61 c mod p 应用三 xf
  • 佩尔方程(超详细推导+例题讲解) 每日一遍,算法再见!

    这里写目录标题 佩尔方程第一类佩尔方程第一类佩尔方程例题讲解 第二类佩尔方程 佩尔方程 第一类佩尔方程 定义 xff1a 形如 x 2 d
  • FFT(傅里叶快速变换,详细讲解+推导) 每日一遍,算法再见!

    FFT详细推导 FFT 傅里叶快速变换 一 前置知识1 复数和单位根2 单位根的三个引理3 多项式 二 FFT 快速傅里叶变换推导 三 IFFT四 FFT求解多项式乘积模板代码1 递归版2 非递归版 这个更快 xff0c 省去了递归时间 五