学习笔记::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... 「学习笔 ...
随机推荐
- C++(二十三) — 内存泄漏及指针悬挂
1.内存泄漏 动态申请的内存空间没有正常释放,但也不能继续使用. ; pch1 = new char('A'); // 此处申请的空间未被释放. char *pch2 = new char; pch1 ...
- Selenium with Python 006 - 操作浏览器
#!/usr/bin/env python # -*- coding: utf-8 -*- from selenium import webdriver import time driver = we ...
- poj3683 2 -sat输出路径
tarjan缩点,拓扑排序染色输出(貌似挑战上面没有拓扑啊,而且这样写还过了= =) 主要是找s,t,d,三者之间的关系,找出合取范式这题就很容易了 #include<map> #incl ...
- java JVM 随笔
先说重点: 对象在堆区 方法在栈区 变量在方法区,常量池在方法区 为什么要了解Java 虚拟机 ? 这个问题一直困惑了我很长一段时间,其实在我们开发的过程中,即使我们不了解JVM也能正常的开发,但是当 ...
- 【Hive】数据类型
1.基本类型 整型:tinyint / samllint / int / bigint 浮点型:float / double / Decimals 布尔型:boolean 字符串:string / v ...
- scrapy 碎片
1.启动命令 2.目录结构 3.文件说明 4.架构图示 5.代码流程 参考资料: http://www.cnblogs.com/yangxt90/articles/9021530.html http: ...
- Python正则表达式使用过程中的小细节
今天用Python写了个简单的爬虫程序,抓取虎扑篮球(nba.hupu.com)的首页内容,代码如下: #coding:gb2312 import urllib2, re webpage = urll ...
- (五)java进制
进制 整数的表示 十进制: 0-9, 满10进1 八进制: 0-7, 满8进1,以0开头 每三位表示一位,三位数中最大的是111,111是7,7是八进制中最大的基数 十六进制:0-15,满 ...
- [EMWIN]FRAMEWIN 与 WINDOW 的使用注意
1.对于window控件,选中这类型控件的时候直接选中对应句柄即可: WM_InvalidateWindow(hWin); WM_SelectWindow(hWin); WM_CreateTimer( ...
- 【学习】JennyHui学自动化测试
学习材料:虫师的Python书,乙醇的教程 Selenium 常用的键盘事件 智能等待 处理富文本框 定位 界面数据与数据库数据对比 Excel操作 下载文件 Selenium 2.0 学习笔记 == ...