学习笔记::fft
上次学fft还是5月份,昨天发现已经忘记怎么推导了,代码也看不懂了,就又学习了一发,大概是看menci的博客
0.fft可以进行多项式乘法,朴素的乘法跟手算一样是O(n^2),fft可以通过分治做到nlogn
1.点值表示:首先我们平常看见的多项式都是系数表示,类似于a0+a1*x^1+a2*x^2......,然而通过这种形式我们是不可能降低复杂度的,怎么搞都是O(n^2),于是我们换成点值表示。点值表示是什么呢?设多项式A(x),那么我们带进去一个x可以得到一个y,y=A(x),然后我们取b个不同的x,也就得到n个不同的y,这两个n维向量就是点值表示(x0,x1,x2,...,xn-1),(y0,y1,y2,...,yn-1),其实也可以看成一个函数上取了n个不同的点。每个点值表示对应了唯一的多项式。
两个点值表达式可以相乘,而且可以O(n)相乘,就是对应项乘对应项,设两个多项式A(x),B(x),乘出来是C(x),假设点值表达式分别是(1,1),(2,2);(2,1),(2,2);那么乘出来就是(2,1),(2,4)。
所以现在的问题就是如何把一个系数表示的多项式变换为点值表达式,和从系数表示变成点值表示,分别叫多项式的求值和插值,考虑朴素的求值就是带进去一个一个算,复杂度是O(n^2)的,插值朴素也很慢,fft可以将这两个过程通过分治优化到O(nlogn),于是我们可以在O(n+nlogn)的时间复杂度解决多项式乘法
也就是说是这样的过程o(nlogn)求值(dft)->O(n)乘法->O(nlogn)插值(idft)完成多项式乘法
2.求值(dft)与插值(idft):为了分治,我们先倍增到2^k,这样很方便。考虑带进去的n个x具体是什么数,我们用单位复数根。单位复数根是一个虚数,就是在复平面上画一个单位圆,满足w^n=1的w就是单位负数根,也就是在单位圆上和x实轴夹角为2*pi/n的那个角。
这个东西有很多好的性质。现在考虑fft,设要求的多项式为A(x),然后我们按下奇偶分类,A(x)=a0+a1*x^1+a2*x^2+...+an-1*x^n-1,这里n都默认为2^k,那么A1(x)=a0+a2*x^2+...+an-2*x^n-2,A2(x)=a1*x+a3*x^3+...+an-1*x^n-1,那么A(x)=A1(x^2)+x*A2(x^2)
带进去单位复数根,A(w(n,k))=A1(w(n,k)^2)+w(n,k)* A2(w(n,k)^2),根据单位负数根的性质,w(n,k)=w(n/2,k/2),那么A(w(n,k))=A1(w(2*n,k))+w(n, k) *A2(w(2*n,k)) = A1(w(n/2,k))+w(n,k)* A2(w(n/2,k))
A(w(n,k+n/2))=A1(w(n,k+n/2))+ w(n,k) *A2(w(n,k+n/2)) 因为W(n,n)=1,w(n,k+n/2)^2=w(n,2*k+n)=w(n,2*k)=w(n/2,k),而w(n,k+n/2)=-w(n,k),因为w(n,n/2)=-1,那么A(w(n,k))=A1(w(n/2,k))- w(n,k)*A2(w(n/2,k)),于是枚举k∈[0,n/2)就可以得到[0,n-1)的所有值,也就是说我们知道了A(w(n/2,0))->A(w(n/2,n/2-1))的所有值后就可以用得出A(w(n,0))->A(w(n,n-1)),那么每次的复杂度是T(n)=2*T(n/2)+O(n)=O(nlogn),解决了求值的问题,至于插值没有搞清楚,就是把-1带入,然后每项除以n,具体没有搞清楚。
然而上面这样是递归的形式,比较慢,我们想可以直接迭代求。
首先我们发现最后每个数最终的位置是原来二进制位置的对称也就是如果01就变成10,也就是2号位和1号位交换,那么我们可以预处理出最终的位置。考虑合并的过程,我们先知道在进行了n=l的合并后,每个数组位置存的是当前这段[i,i+l)的求值后的值,也就是A1和A2的值,那么现在我们把长度乘2合并。
考虑合并的时候的两个式子
B(w(n,k)) = A1(w(n/2,k))+w(n,k)* A2(w(n/2,k))
B(w(n,k+n/2))=A1(w(n/2,k))- w(n,k)*A2(w(n/2,k))
我们要多开一个b保存,其实不用,我们记录x=A1(w(n/2,k)),y=w(n,k)* A2(w(n/2,k)),然后直接A1=x+y,A2=x-y就行了。
板子:记住循环要循环到n,否则最后取不到
#include<bits/stdc++.h>
using namespace std;
#define pi acos(-1)
const int N = 3e5 + ;
int n1, n2, n, k;
complex<double> a[N], b[N];
void fft(complex<double> *a, int f)
{
for(int i = ; i < n; ++i)
{
int t = ;
for(int j = ; j < k; ++j) if(i >> j & ) t |= << (k - j - );
if(i < t) swap(a[i], a[t]);
}
for(int l = ; l <= n; l <<= )
{
int m = l >> ;
complex<double> w(cos(pi / m), f * sin(pi / m));
for(int i = ; i < n; i += l)
{
complex<double> t(, );
for(int k = ; k < m; ++k, t *= w)
{
complex<double> x = a[i + k], y = t * a[i + m + k];
a[i + k] = x + y;
a[i + m + k] = x - y;
}
}
}
}
int main()
{
scanf("%d%d", &n1, &n2);
for(int i = ; i <= n1; ++i)
{
int x;
scanf("%d", &x);
a[i] = x;
}
for(int i = ; i <= n2; ++i)
{
int x;
scanf("%d", &x);
b[i] = x;
}
n = ;
while(n <= n1 + n2) n <<= , ++k;
fft(a, );
fft(b, );
for(int i = ; i < n; ++i) a[i] = a[i] * b[i];
fft(a, -);
for(int i = ; i <= n1 + n2; ++i) printf("%d ", (int)(a[i].real() / n + 0.5));
return ;
}
ntt
取模怎么办呢
我们要用ntt
我们就不能用单位根了,这样不能取模,于是我们用原根,原根和单位跟性质很想。
单位根w(n,k)
1.w(n,n)=1
2.w(n,n/2)=-1->w(n.k+n/2)=-w(n,k)
3.w(n,k)=w(n/2,k/2)
我们要原根有这些性质
定义原根g满足g^0,g^1,...g^n-2%P各不相同
设一个质数为p=n*q+1,其中n=2^k,则g^(n*q)=1(mod p),这是由费马小定理 a^(p-1) = 1 (mod p)
那么变成单位根
单位根w(n,k)相当于把一个圆分成n分取其中k份的值,也就是一个单位是1/n个圆
那么这里我们定义g(n,k)表示一份是g^q,取k个,g(n,n)就是g^n*q
又因为费马小定理所以g(n,n)=1 (mod p)
并且g(n,n)=g(n,n/2)^2所以g(n,n/2)=1或-1.又因为%p各不相同,并且g^0%p=1,所以g(n,n/2)%p=-1,满足单位根性质
学习笔记::fft的更多相关文章
- [学习笔记]FFT——快速傅里叶变换
大力推荐博客: 傅里叶变换(FFT)学习笔记 一.多项式乘法: 我们要明白的是: FFT利用分治,处理多项式乘法,达到O(nlogn)的复杂度.(虽然常数大) FFT=DFT+IDFT DFT: 本质 ...
- FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅲ
第三波,走起~~ FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅰ FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅱ 单位根反演 今天打多校时 1002 被卡科技了 ...
- [学习笔记]NTT——快速数论变换
先要学会FFT[学习笔记]FFT——快速傅里叶变换 一.简介 FFT会爆精度.而且浮点数相乘常数比取模还大. 然后NTT横空出世了 虽然单位根是个好东西.但是,我们还有更好的东西 我们先选择一个模数, ...
- [学习笔记] 多项式与快速傅里叶变换(FFT)基础
引入 可能有不少OIer都知道FFT这个神奇的算法, 通过一系列玄学的变化就可以在 $O(nlog(n))$ 的总时间复杂度内计算出两个向量的卷积, 而代码量却非常小. 博主一年半前曾经因COGS的一 ...
- 【学习笔记】快速傅里叶变换(FFT)
[学习笔记]快速傅里叶变换 学习之前先看懂这个 浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理--gzy hhh开个玩笑. 讲一下\(FFT\) ...
- 快速傅里叶变换(FFT)学习笔记
定义 多项式 系数表示法 设\(A(x)\)表示一个\(n-1\)次多项式,则所有项的系数组成的\(n\)维向量\((a_0,a_1,a_2,\dots,a_{n-1})\)唯一确定了这个多项式. 即 ...
- FFT和NTT学习笔记_基础
FFT和NTT学习笔记 算法导论 参考(贺) http://picks.logdown.com/posts/177631-fast-fourier-transform https://blog.csd ...
- 「学习笔记」FFT 之优化——NTT
目录 「学习笔记」FFT 之优化--NTT 前言 引入 快速数论变换--NTT 一些引申问题及解决方法 三模数 NTT 拆系数 FFT (MTT) 「学习笔记」FFT 之优化--NTT 前言 \(NT ...
- 「学习笔记」FFT 快速傅里叶变换
目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...
随机推荐
- activity启动模式之singleTask
activity启动模式之singleTask 一.简介 如果另外一个应用调用了C2,C2在栈底,如果这个程序里面再嗲用C1,C3,C2,那么这个C2就是调用onNewIntant的,C1和C3都被销 ...
- 分享知识-快乐自己:能使 Oracle 索引失效的六大限制条件
Oracle 索引的目标是避免全表扫描,提高查询效率,但有些时候却适得其反. 例如一张表中有上百万条数据,对某个字段加了索引,但是查询时性能并没有什么提高,这可能是 oracle 索引失效造成的.or ...
- serialVersionUID 作用
一个类在序列化时,用来记录当前类的版本号.在反序列化时,首先获取当前类的版本号,不一致则报异常. 若版本号一致,但两个类不一致.写肯定全部写进去.但提取时 类若没有这个字段,则不提取.若提取类多个字段 ...
- PHP中的定界符
因为PHP是一个Web编程语言,在编程过程中难免会遇到用echo来输出大段的html和javascript脚本的情况,如果用传统的输出方法——按字符串输出的话,肯定要有大量的转义符来对字符串中的引号等 ...
- 《The Cg Tutorial》阅读笔记——动画 Animation
这段时间阅读了英文版的NVidia官方的<The Cg Tutorial>,借此来学习基本的图形学知识和着色器编程. 在此做一个阅读笔记. 本文为大便一箩筐的原创内容,转载请注明出处,谢谢 ...
- 23-THREE.JS 光照材质
<!DOCTYPE html> <html> <head> <title></title> <script src="htt ...
- postgresql recovery.conf改变需要重启吗
之前在研究pgpoll时,发现trigger_file参数指定的文件存在后,会自动将standby节点提升为可写节点.不需要手动执行pg_ctl promote,但是这个时间一般有延迟,因为进程会定期 ...
- Electron 使用 Webpack2 打包多入口应用程序
Electron 使用 Webpack2 打包多入口应用程序 接前面一篇文章,前一篇文章中只有一个页面,并且只有一个js文件,所以打包的时候会把那个js打包成一个bundle.js文件.但是假如我们有 ...
- Reinforcement Learning Q-learning 算法学习-4
Q-learning 相关的资料 https://www.youtube.com/watch?v=V1eYniJ0Rnk google deepmind 的Q-learning 算法打游戏的一个很酷的 ...
- LINUX 修改本机yum源为163镜像源
进入yum源配置目录 [root@localhost yum.repos.d]# cd /etc/yum.repos.d/ 备份系统yum源,用于日后恢复. [root@localhost yum.r ...