学习笔记::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... 「学习笔 ...
随机推荐
- 【scala】集合框架
- 修改MAC过程
首先打开PC的Telnet功能,如下: 对PC设置本地IP 2.cmd→telnet 192.168.1.230(出厂默认IP) 3.root →密码:20...................(公司 ...
- ubuntu下搭建hadoop平台
终于把单击模式跟伪分布式模式搭建起来了,记录于此. 1.SSH无密码验证配置 因为伪分布模式下DataNode和NameNode均是本身,所以必须配置SSH localhost的无密码验证. 第一步, ...
- python基础之协程,列表生成式,生成器表达式
三元表达式 #三元表达式 name = 'alex' name = 'egon' res='SB' if name == 'alex'else 'shuai' print(res)#输出:shuai ...
- webservice SOAP WSDL UDDI简介
WebServices简介 先给出一个概念 SOA ,即Service Oriented Architecture ,中文一般理解为面向服务的架构, 既然说是一种架构的话,所以一般认为 SOA 是包含 ...
- FlashDevelop安装配置
1.下载 FlashDevelop,flash sdk,flash sdk debug,.net framework,java sdk(32位,不管机器是多少位,否则不能单步调试的) 2. 安装Fl ...
- 《Drools7.0.0.Final规则引擎教程》第3章 3.2 KIE API解析
3.2.4 KieServices 该接口提供了很多方法,可以通过这些方法访问KIE关于构建和运行的相关对象,比如说可以获取KieContainer,利用KieContainer来访问KBase和KS ...
- 毕业生、程序猿转岗该如何选择Java、大数据和VR?答案在这里!
许久不见的朋友请我吃饭,期间给我介绍他一个弟弟,说明年要毕业了,还不知道找啥工作,说有培训机构让他学VR.大数据什么的,不知道前景咋样,想咨询一下我.相信很多朋友面临毕业,都不知道该从事哪个行业,自己 ...
- kali视频学习(6-10)
第三周 kali视频(6-10)学习 6.信息搜集之主机探测 7.信息搜集之主机扫描(nmap使用) 8.信息搜集之指纹识别 9.信息搜集之协议分析 10.漏洞分析之OpenVAS安装 6.信息搜集之 ...
- 在Mac中安装python,配置python环境
参考链接:http://blog.justbilt.com/2014/07/02/setup_python_on_mac/ 其实跟windows差不多,就是在python官网下载特定版本的python ...