【总结】对FFT的理解 / 【洛谷 P3803】 【模板】多项式乘法(FFT)
题目链接
\(\Huge\text{无图,慎入}\)
\(FFT\)即快速傅里叶变换,用于加速多项式乘法。
如果暴力做卷积的话就是一个多项式的每个单项式去乘另一个多项式然后加起来,时间复杂度为\(O(n^2)\)。
\(FFT\)算法基本思想是把系数表达式转换成点值表达式,求出卷积的点值表达式,再转换回系数表达式。
何为点值表达式?
把多项式看成一个函数,比如\(n\)次多项式\(F\)可以看成一个\(n\)次函数\(F(x)=a_0+a_1x+a_2x^2+\cdots +a_nx^n\)
众所周知,知道\(n\)次函数上\(n+1\)个点的坐标一定可以求出这个\(n\)次函数的解析式。
用我们学过的知识一次函数、二次函数都可以验证。
硬要扩展到任意次函数的话也好解释,可以得到\(n+1\)个方程,用高斯消元就能解出来,当然肯定不会在\(FFT\)算法里出现,因为算法的目的是加速。
系数表达式->点值表达式的过程叫\(DFT\),点值表达式->系数表达式的过程叫\(IDFT\)。
先讲\(DFT\)。
怎么系数->点值?
代\(n\)个点是最直接的办法,但显然求一个点的值就是\(O(n)\)的,总时间复杂度为\(O(n^2)\)
这里要引入单位根概念,请确保了解复数的概念。
\(n\)次单位根记作\(\omega_n\),定义为\(n\)次方等于\(1\)的复数。
来推导一下性质。
首先\(n\)次方等于\(1\),这个复数的模长肯定是等于\(1\)的,所以在单位圆上。
其次,幅角\(\times n=2k\pi,k\in Z\)
脑补一下可以发现,\(n\)次单位根\(n\)等分单位圆,且\(1\)是一条等分线。
\(\omega_n^k\)表示从\(1\)开始逆时针旋转第\(i\)个\(n\)次单位根(从\(0\)开始)。
例如\(\omega_3^2\)就是把单位圆三等分,原点向正方向的射线是一条等分线,位于\(x\)轴下方的那条等分线。
不难发现,\(\forall n,\omega_n^0=1\) \(\forall n=2k,k\in Z, \omega_n^{\frac{n}{2}}=-1\)
理论上\(k\in [0,n)\),但类似于角度,也会出现超过\(360°\)或者负数的情况,同理也有\(\omega_n^k=\omega_n^{k\%n}\)
单位根的性质:
\(\omega_n^{a+b}=\omega_n^a\times \omega_n^b\),这个的解释就是复数相乘模长相乘幅角相加的法则。
有了这条,就能推出其他性质了。
\(\omega_n^k=\omega_{dn}^{dk}\)
\((\omega_n^k)^j=\omega_n^{jk}\)
步入正题:
\(DFT:\)
我们需要将\(n\)项多项式\(F(x)=a_0+a_1x+a_2x^2+\cdots +a_{n-1}x^{n-1}\)转成点值表达式。
假设\(n\)是\(2\)的正整数次幂。
设
\(FL(x)=a_0+a_2x+a_4x^2+\cdots+a_{n-2}x^{\frac{n}{2}-1}\)
\(FR(x)=a_1+a_3x+a_5x^2+\cdots+a_{n-1}x^{\frac{n}{2}-1}\)
易得\(F(x)=FL(x^2)+xFR(x^2)\)(自己代进去算一边就行了)
用\(\omega_n^k\)(\(k<\frac{n}{2}\))代入这个式子
\(\begin{align}F(\omega_n^k)&=FL(\omega_n^{2k})+\omega_n^kFR(\omega_n^{2k})\\&=FL(\omega_{\frac{n}{2}}^{k})+\omega_n^kFR(\omega_{\frac{n}{2}}^{k})\end{align}\)
这是\(k<\frac{n}{2}\)的情况,那如果\(k>=\frac{n}{2}\)呢?
\(\begin{align}F(\omega_n^{k+\frac{n}{2}})&=FL(\omega_n^{2k+n})+\omega_n^{k+\frac{n}{2}}FR(\omega_n^{2k+n})\\&=FL(\omega_{\frac{n}{2}}^{k})-\omega_n^kFR(\omega_{\frac{n}{2}}^{k})\end{align}\)
\(P.S:\omega_n^{k+\frac{n}{2}}=\omega_n^k\times \omega_n^{\frac{n}{2}}=-\omega_n^k\)
所以,如果我们知道了\(FL(\omega_{\frac{n}{2}}^{k})和FR(\omega_{\frac{n}{2}}^{k})\),就能求出\(F(\omega_n^k)\)和\(F(\omega_n^{k+\frac{n}{2}})\)
但是,我们怎么知道\(FL(\omega_{\frac{n}{2}}^{k})和FR(\omega_{\frac{n}{2}}^{k})\)呢?
难道这不像归并排序吗,分治啊!
实现过程中可能遇到的问题\(FAQ\)
1、怎么求\(\omega_n^k\)
只需要求出\(\omega_n^1\)他的\(k\)次方即\(\omega_n^k\)
2、怎么求\(\omega_n^1\)
\(\omega_n^1\)与\(1\)的夹角我们知道:\(\frac{2\pi}{n}\),然后又在单位圆上,解三角形啊!
告诉你结论就是\(\omega_n^1=cos(\frac{2\pi}{n})+sin(\frac{2\pi}{n})i\)
3、\(FL,FR\)数组从哪来
如果在递归中定义这两个数组显然会炸空间,于是蝴蝶操作诞生了。
咕咕咕。
但是,实际中\(n\)不一定是\(2\)的正整数次幂,我们只需要在最高位补\(0\)就行了,不影响结果。
现在我们求出了多项式的点值表达式,但这和他们的卷积有什么关系呢?
设\(H=F\times G\),\(H,F,G\)均为多项式
则\(H(x)=F(x)\times G(x)\)
所以如果我们知道两个多项式在\(x=\omega_n^0,\omega_n^1,\cdots,\omega_n^{n-1}\)的值,就能求出他们的卷积在\(x=\omega_n^0,\omega_n^1,\cdots,\omega_n^{n-1}\)的值,于是,第一步完成。
\(IDFT:\)
咕咕咕。
#include <cstdio>
#include <cmath>
#include <algorithm>
#define re register
using namespace std;
const int MAXN = 3000010;
const double PI = M_PI;
struct complex{
double x, y;
complex(double xx = 0, double yy = 0){ x = xx; y = yy; }
}a[MAXN], b[MAXN];
inline complex operator + (complex a, complex b){
return complex(a.x + b.x, a.y + b.y);
}
inline complex operator - (complex a, complex b){
return complex(a.x - b.x, a.y - b.y);
}
inline complex operator * (complex a, complex b){
return complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
}
inline int read(){
re int s = 0, w = 1;
re char ch = getchar();
while(ch < '0' || ch > '9'){ ch = getchar(); if(ch == '-') w = -1; }
while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); }
return s * w;
}
int r[MAXN], n, m;
void FFT(complex *f, int mode){
for(re int i = 0; i < n; ++i) if(i < r[i]) swap(f[i], f[r[i]]);
for(re int p = 2; p <= n; p <<= 1){
re int len = p >> 1;
re complex tmp(cos(PI / len), mode * sin(PI / len));
for(re int l = 0; l < n; l += p){
re complex w(1, 0);
for(re int k = l; k < l + len; ++k){
re complex t = w * f[len + k];
f[len + k] = f[k] - t;
f[k] = f[k] + t;
w = w * tmp;
}
}
}
}
inline double d(double x){
if(fabs(x) < 1e-9) return 0;
return x;
}
int main(){
n = read(); m = read();
for(re int i = 0; i <= n; ++i) a[i].x = read();
for(re int i = 0; i <= m; ++i) b[i].x = read();
for(m += n, n = 1; n <= m; n <<= 1);
for(re int i = 1; i < n; ++i) r[i] = r[i >> 1] >> 1 | ((i & 1) * (n >> 1));
FFT(a, 1); FFT(b, 1);
for(re int i = 0; i < n; ++i) a[i] = a[i] * b[i];
FFT(a, -1);
for(int i = 0; i <= m; ++i) printf("%.0f ", d(a[i].x / n));
return 0;
}
【总结】对FFT的理解 / 【洛谷 P3803】 【模板】多项式乘法(FFT)的更多相关文章
- 洛谷.3803.[模板]多项式乘法(FFT)
题目链接:洛谷.LOJ. FFT相关:快速傅里叶变换(FFT)详解.FFT总结.从多项式乘法到快速傅里叶变换. 5.4 又看了一遍,这个也不错. 2019.3.7 叕看了一遍,推荐这个. #inclu ...
- 洛谷.3803.[模板]多项式乘法(NTT)
题目链接:洛谷.LOJ. 为什么和那些差那么多啊.. 在这里记一下原根 Definition 阶 若\(a,p\)互质,且\(p>1\),我们称使\(a^n\equiv 1\ (mod\ p)\ ...
- P3803 [模板] 多项式乘法 (FFT)
Rt 注意len要为2的幂 #include <bits/stdc++.h> using namespace std; const double PI = acos(-1.0); inli ...
- FFT/NTT总结+洛谷P3803 【模板】多项式乘法(FFT)(FFT/NTT)
前言 众所周知,这两个东西都是用来算多项式乘法的. 对于这种常人思维难以理解的东西,就少些理解,多背板子吧! 因此只总结一下思路和代码,什么概念和推式子就靠巨佬们吧 推荐自为风月马前卒巨佬的概念和定理 ...
- 洛谷.4512.[模板]多项式除法(NTT)
题目链接 多项式除法 & 取模 很神奇,记录一下. 只是主要部分,更详细的和其它内容看这吧. 给定一个\(n\)次多项式\(A(x)\)和\(m\)次多项式\(D(x)\),求\(deg(Q) ...
- 洛谷.4238.[模板]多项式求逆(NTT)
题目链接 设多项式\(f(x)\)在模\(x^n\)下的逆元为\(g(x)\) \[f(x)g(x)\equiv 1\ (mod\ x^n)\] \[f(x)g(x)-1\equiv 0\ (mod\ ...
- 洛谷 P4512 [模板] 多项式除法
题目:https://www.luogu.org/problemnew/show/P4512 看博客:https://www.cnblogs.com/owenyu/p/6724611.html htt ...
- 洛谷 P4238 [模板] 多项式求逆
题目:https://www.luogu.org/problemnew/show/P4238 看博客:https://www.cnblogs.com/xiefengze1/p/9107752.html ...
- 洛谷p3803 FFT入门
洛谷p3803 FFT入门 ps:花了我一天的时间弄懂fft的原理,感觉fft的折半很神奇! 大致谈一谈FFT的基本原理: 对于两个多项式的卷积,可以O(n^2)求出来(妥妥的暴力) 显然一个多项式可 ...
随机推荐
- SSH框架配置
--------------------------------applicationContext.xml-------------------------------- <?xml vers ...
- 在MySQL中如何使用覆盖索引优化limit分页查询
背景 今年3月份时候,线上发生一次大事故.公司主要后端服务器发生宕机,所有接口超时.宕机半小时后,又自动恢复正常.但是过了2小时,又再次发生宕机. 通过接口日志,发现MySQL数据库无法响应服务器.在 ...
- javascript之彻底理解this
彻底理解this,需要彻底理解函数 函数是复杂类型,存储在堆中. 函数是独立的, 对象中的方法只是对象中有个函数的引用 函数被调用时,调用者会像被调用者提供个上下文环境, 这个环境就是this 构造 ...
- uva1391-Astronauts
宇航员执行任务,有三个任务ABC.把宇航员按照平均年龄分成新老两组,老宇航员可以去AC,新宇航员可以取BC.宇航员之间有不能共存关系,问是否有合法的分配方案. 分析 虽然有三个任务,但每个宇航员还是只 ...
- 口胡:[HNOI2011]数学作业
题面 一开始看这题看了好久--觉得这题不可做. 结果是看错题了,我居然看着一段长长的C开头的单词,然后就觉得这是卡特兰数--不知道我在想些什么-- 观察到对于 i = 1~9 : f[i] = f[i ...
- Jsp遍历后台传过来的List
1:使用jstl标签 (可以和自定义标签配合使用) 首先引用jstl标签 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" ...
- 【BZOJ4456】旅行者(最短路,分治)
[BZOJ4456]旅行者(最短路,分治) 题面 BZOJ Description 小Y来到了一个新的城市旅行.她发现了这个城市的布局是网格状的,也就是有n条从东到西的道路和m条从南到北 的道路,这些 ...
- 【BZOJ1758】【WC2010】重建计划(点分治,单调队列)
[BZOJ1758][WC2010]重建计划(点分治,单调队列) 题面 BZOJ 洛谷 Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表 ...
- python基础----__slots__方法、__call__方法
''' 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) 2.引子:使用点来访问属性本质就是在访问类或者对象的_ ...
- python基础----常用模块
一 time模块(时间模块)★★★★ 时间表现形式 在Python中,通常有这三种方式来表示时 ...