快速傅里叶变换 学习笔记

news/2024/9/12 15:15:03/文章来源:https://www.cnblogs.com/Cyanwind/p/18368245

快速傅里叶变换 学习笔记

简介:

卷积是形如 \(C_k=\sum_{i\oplus j==k} A_i*B_j\) 的式子,其中 \(\oplus\) 为表示某种运算。

而快速傅里叶变换(FFT)用于在 \(O(n\log n)\) 的时间复杂度内求加法卷积。

对于一个 \(k\) 次多项式,如果我们知道了它在 \(k+1\) 个点上的值,那么我们可以求出这个多项式。而两个多项式在同一个点上的值相乘的话,就可以得到 这两个多项式相乘后的多项式 在这个点上的值。FFT 的主要思路就是先将两个多项式在若干个点上的取值,然后相乘,再通过点值求出两个多项式相乘的结果。

单位根

先考虑求点值。我们肯定不能暴力选择点去代入求值,这样的时间复杂度是 \(O(n^2)\) 的,要找一些有特殊性质的点去代入。

于是我们在复数域找到了单位根,它是在复平面上的单位圆上的点,表示为 \(\omega_n^k\) ,它的意义是将单位圆分割成 \(n\) 份,按顺指针方向的第 \(k\) 个点所表示的值。两个复数相乘时,它们的辐角相加(从实轴的正半轴开始,按逆时针旋转),模长相乘(该点到原点的距离)。于是我们可以得到单位根的以下性质:

\[\omega_n^i*\omega_n^j=\omega_n^{i+j}\\ \omega_n^k=\omega_n^{k\%n}\\ \omega_{n*i}^{k*i}=\omega_n^k\\ \omega_n^{k+n/2}=-\omega_n^k(当 n 为偶数时) \]

对于最后一条,我们可以将 \(n/2\) 看做旋转半个圆周,所以就是取相反数。

对于一个 \(n\) 次多项式,我们看看将单位根代入会有什么性质:

先将多项式 \(F[x]\) 拆成两个部分: \(FL[x]=x^0+x^2+x^4...,FR[x]=x^1+x^3+x^5...\) 。然后就可以将多项式 \(F[x]\) 这样表示:

\[F[x]=FL[x^2]+x*FR[x^2] \]

再分别将 \(\omega_n^k\)\(\omega_n^{k+n/2}\) 代入:

\[F[\omega_n^k]=FL[(\omega_n^k)^2]+\omega_n^k*FR[(\omega_n^k)^2]\\ =FL[\omega_{n/2}^k]+\omega_n^k*FR[\omega_{n/2}^k]\\ F[\omega_n^{k+n/2}]=FL[(\omega_n^{k+n/2})^2]+\omega_n^{k+n/2}*FR[(\omega_n^{k+n/2})^2]\\ =FL[\omega_{n/2}^{k+n/2}]-\omega_n^k*FR[\omega_{n/2}^{k+n/2}]\\ =FL[\omega_{n/2}^k]-\omega_n^k*FR[\omega_{n/2}^k] \]

发现上下两式只有后面一项的符号不同,所以如果我们求出了 \(FL[x]\)\(FR[x]\)\(\omega_{n/2}^{0..n/2-1}\) 上的值,我们就可以求出 \(F[x]\)\(\omega_n^{0...n-1}\) 上的值了,这样做一次的时间复杂度是 \(O(n)\) 的。同时我们可以发现,对于 \(FL[x]\)\(FR[x]\) ,其实可以看做一个相同的子任务,再次递归下去求解。一共需要递归 \(log\ n\) 次。

但是此时我们又有一个问题:如果某个 \(n\) 不能被整除了怎么办?我们可以给它在后面补位,系数设为 0 就行。再观察一下我们递归到最下面一层时,系数的编号与第一层相比有什么特点。可以发现它是二进制位的反转,所以我们可以用数位DP的方法求出反转后的编号,这样就不用一遍一遍递归下去求了。

tr[i]=(tr[i>>1]>>1)|(i&1)?limit>>1:0

求多项式

通过上面的步骤,我们已经将 \(n\) 个点的值求出来了,接下来就要通过这 \(n\) 个点的值求多项式。

设点值构成的序列为 \(G[x]\) 。然后我们可以列出以下式子:

\[G[x]=\sum_{i=0}^{n-1}\omega_n^i*F[i] \]

然后可以得到以下结论:

\[F[x]*n=\sum_{i=0}^{n-1}\omega_n^{-i}*G[i] \]

这个式子的证明就是将上面的式子代入。此时我们发现,这个式子和我们求点值的式子十分相似,只是在单位根上有点区别。我们可以发现,在单位圆上, \(\omega_n^{-1}\) 所在的点和 \(\omega_n^1\) 所在的点,它们关于实轴对称。于是我们也可以用同样的方法来求上面的式子。最后记得除以 \(n\) 就是我们最终的多项式。

Code

#include<cstdio>
#include<cmath>
using namespace std;
const int N=3e6+5;const double PI=6.28318530717958646;
struct Date{double x,y;Date operator*(Date v) {Date res;res.x=x*v.x-y*v.y;res.y=x*v.y+y*v.x;return res;};Date operator+(Date v) {Date res;res.x=x+v.x;res.y=y+v.y;return res;};Date operator-(Date v) {Date res;res.x=x-v.x;res.y=y-v.y;return res;};
};Date f[N],g[N],w[N];
int tr[N],n,m;
void swap(double &x,double &y)
{double t=x;x=y;y=t;
}
void FFT(int flag)
{int i,j,len;Date t;w[0].x=1;w[0].y=0;w[1].x=cos(PI/n);w[1].y=sin(PI/n)*flag;for(i=1;i<n;i++)w[i]=w[i-1]*w[1];for(i=0;i<n;i++){if(i<tr[i]){swap(f[i].x,f[tr[i]].x);swap(f[i].y,f[tr[i]].y);}}for(len=1;len<n;len=len<<1){for(i=0;i<n;i+=2*len){for(j=0;j<len;j++){t=f[i+j+len]*w[n/len/2*j];f[i+j+len]=f[i+j]-t;f[i+j]=f[i+j]+t;}}}
}
int main()
{int i,h,len;scanf("%d%d",&n,&m);for(i=0;i<=n;i++)scanf("%lf",&f[i].x);for(i=0;i<=m;i++)scanf("%lf",&g[i].x);h=0;len=n+m;while((1<<h)<n+m+1)h++;n=1<<h;for(i=0;i<n;i++)tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);FFT(1);for(i=0;i<n;i++){swap(f[i].x,g[i].x);swap(f[i].y,g[i].y);}FFT(1);for(i=0;i<n;i++)f[i]=f[i]*g[i];FFT(-1);for(i=0;i<=len;i++)printf("%d ",int(f[i].x/n+0.49));return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/784023.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试场景题:一次关于线程池使用场景的讨论。

你好呀,我是歪歪。 来一起看看一个关于线程池使用场景上的问题,就当是个场景面试题了。 问题是这样的:字有点多,我直接给你上个图你就懂了:前端发起一个生成报表页面的请求,这个页面上的数据由后端多个接口返回,另外由于微服务化了,所以数据散落在每个微服务中,因此需…

R语言VAR模型的多行业关联与溢出效应可视化分析

全文链接:https://tecdat.cn/?p=37397 原文出处:拓端数据部落公众号 摘要:本文对医疗卫生、通信、金融、房地产和零售等行业的数据展开深入研究。通过读取数据、计算收益率、构建 VAR 模型并进行估计,进一步分析各行业变量的影响及残差的协方差与相关矩阵。同时,计算传…

【流程化办公利器】可视化表单拖拽生成器优势多

一起来了解可视化表单拖拽生成器的诸多优势特点,看看它是如何给企业高效助力的。当前,实现流程化办公早已成为大家的发展愿景。低代码技术平台拥有可视化操作界面、够灵活、易操作等多个优势特点,可以在推动企业实现降本、增效、提质等方面全力护航,是不多得的优质软件平台…

C#上传excel,解析主从表,1W数据快速插入数据库,5s完成

参考文章 net core天马行空系列-各大数据库快速批量插入数据方法汇总 ExcelMapperController核心代码 [HttpPost] public async Task<IActionResult> ImportToDoItems(IFormFile file) {if (file == null || file.Length == 0){return BadRequest("File is empty&qu…

Linux scp 文件传输

scp将本服务器的文件传输到远程服务器 基本语法 scp `[源路径]` `[目标服务器]`:`[目标路径]`样例 将本服务器123.txt文件传输到远程服务器并重命名为456.txt scp 123.txt user@remote_server:/home/tabu/456.txt使用-r选项复制整个目录 scp -r tabu/* user@remote_server:/hom…

Android libusb

一、环境:配置NDK环境 1、下载libusb源码: https://github.com/libusb/libusb/releases,如下图所示2、删除一些和Android平台无关的文件,删除后的文件如下图所示:思考问题:Android是怎么获取usb设备?如上图所示:连接adb shell,然后cd到/sys/bus/usb/devices/目录,命令…

《花100块做个摸鱼小网站! 》第三篇—热搜表结构设计和热搜数据存储

⭐️基础链接导航⭐️ ☁️ 阿里云活动地址 🐟 上班摸鱼小网站地址 💻 源码库地址一、前言 大家好呀,我是summo,第一篇已经教会大家怎么去阿里云买服务器,以及怎么搭建JDK、Redis、MySQL这些环境。第二篇我们把后端的应用搭建好了,并且完成了第一个爬虫(抖音)。那么这一…

关于STM32H750打破flash--2M限制的简单办法

STM32H750VBTx的flash官方规定只能使用128K的flash,但是其实是可以绕过限制,使用其片内2M的flash空间。 这里介绍一种较为简单的实现的办法,这个办法不同网络上介绍的办法,可以在keil上较轻松地实现。因为它可以使用较高STM32CubeMX(6.12.0)和keil(5.29)的版本。 首先按…

c语言中读入整型数据和浮点型数据

001、读入整型数据[root@PC1 test]# ls test.c [root@PC1 test]# cat test.c ## 测试脚本 #include <stdio.h>int main(void) {int i; //声明整型变量puts("please input an integer.");printf("input an i…

CSP24

学了些DP 学校题库有\(BUG\)首先要满足条件\(x,y\)的二进制有1的位必然包含\(a\),然后让\(s-2a\),也就是除去二进制包含\(a\)有1的位,然后\(<0\)肯定无解,其次是如果有与\(a\)同一级的含\(1\)二进制位也不合法点击查看代码 #include <bits/stdc++.h> #define speed()…