FFT入门
这篇文章会讲讲FFT的原理和代码。
先贴picks博客(又名FFT从入门到精通):http://picks.logdown.com/posts/177631-fast-fourier-transform
首先FFT是干嘛用的?
额其实在oi中它就是一个用来算“快速卷积”的东西。
卷积是啥?
给定两个数组a、b,求数组c使得:
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(i+j<n) c[i+j]+=a[i]*b[j];
这就叫做长度为n的“卷积”。
正常模拟是O(n^2)的,这时候我们就可以用FFT来加速到O(nlogn)!
我们发现,如果我们令a[i]为x^i的系数,那么a、b就可以表示为一个多项式,c就可以被表示为这两个多项式的乘积。
首先我们可以发现,我们对于一个n次多项式,可以用一个多项式的形式来表示它,也可以找到n个位置的值,这样也可以唯一确定这个多项式。
所以我们就初步有了一个思路,我们找到a、b在n个点处的取值,乘在一起,搞回去确定c的多项式形式。
为了和谐,我们一般令n为2的次幂。(注意)
关于这个东西一般有两种写法,一般被称为复数FFT和NTT。
先讲NTT好了......
假设a、b都是整系数多项式,然后模数P十分刺激,满足P是质数,$2^k|P-1$且$2^k>n$时,我们就可以使用NTT。
然后你还要知道原根的有关概念...简单来说就是原根的次幂在模P意义下循环节为$\varphi(P)$,对于素数来说就是P-1。
这里就说一点,998244353的原根是3...
设g为P的原根,那么我们令$\omega_n=g^{\frac{P-1}{n}}$,可以发现:
$\omega_{2n}^{2m}=\omega_{n}^m$,$\omega_{2n}^m=-\omega_{2n}^{m+n}$。(确实挺显然的)
那么我们取$\omega_n^k$,其中k∈{0...n-1},作为n个点,如何算出这n个点处的取值呢?
我们假设偶次项提出来作为a0,奇次项提出来作为a1。
(例如1+2x+3x^2+4x^3,偶次项提出来为1+3x,奇次项提出来为2+4x,注意这里的次数也要相应改变)
那么我们可以发现
所以我们可以用a0和a1的点值表示算出a的点值表示。
T(n)=2T(n/2)+O(n),由主定理复杂度为O(nlogn)。
接下来转回去的话,由于某种奇怪的性质(详细证明可以看picks博客),我们只要用$\omega_{n}^{-m}$代替原来的$\omega_n^{m}$,带进去,最后除以n就行了。即把那一堆$\omega$翻转一下。
当然如果你真这样瞎搞常数似乎真的挺大的,事实上有一些更靠谱的做法,上图:

开始我们把输入的数二进制位翻转,就可以得到左边,然后按这个图上进行蝶形运算(就是刚才那两个公式)就可以算出结果了。
额复数FFT更加简单。
我们令$\omega_{n}$为单位根,即满足$x^n=1$的复数,它可以看做复平面上x轴正方向绕逆时针方向旋转$\frac{2\pi}{n}$的复数。所以$\omega_n=cos(\frac{2\pi}{n})+sin(\frac{2\pi}{n})i$。
听起来十分靠谱...但是这种东西毕竟自己瞎写的话常数实在太大了...
下面这个是n+e的NTT模板,有改动,uoj34:
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
using namespace std;
#define ll long long
ll MOD=998244353;
ll w[2][666666];
ll qp(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%MOD;
a=a*a%MOD; b>>=1;
}
return ans;
}
int K;
void fftinit(int n)
{
for(K=1;K<n;K<<=1);
w[0][0]=w[0][K]=1;
ll g=qp(3,(MOD-1)/K); //3是原根
for(int i=1;i<K;i++) w[0][i]=w[0][i-1]*g%MOD;
for(int i=0;i<=K;i++) w[1][i]=w[0][K-i];
}
void fft(int* x,int v)
{
for(int i=0,j=0;i<K;i++)
{
if(i>j) swap(x[i],x[j]);
for(int l=K>>1;(j^=l)<l;l>>=1);
}
for(int i=2;i<=K;i<<=1)
{
for(int j=0;j<K;j+=i)
{
for(int l=0;l<i>>1;l++)
{
ll t=(ll)x[j+l+(i>>1)]*w[v][K/i*l]%MOD;
x[j+l+(i>>1)]=(x[j+l]-t+MOD)%MOD;
x[j+l]=(x[j+l]+t)%MOD;
}
}
}
if(!v) return;
ll rv=qp(K,MOD-2);
for(int i=0;i<K;i++) x[i]=x[i]*rv%MOD;
}
int N,M,a[666666],b[666666],c[666666];
int main()
{
scanf("%d%d",&N,&M);
++N; ++M; int t=N+M-1;
for(int i=0;i<N;i++) scanf("%d",a+i);
for(int i=0;i<M;i++) scanf("%d",b+i);
fftinit(t); fft(a,0); fft(b,0);
for(int i=0;i<K;i++) c[i]=(ll)a[i]*b[i]%MOD;
fft(c,1);
for(int i=0;i<t;i++) printf("%d ",c[i]);
}
FFT入门的更多相关文章
- TOT 傅立叶变换 FFT 入门
HDU 1402,计算很大的两个数相乘. FFT 只要78ms,这里: 一些FFT 入门资料:http://wenku.baidu.com/view/8bfb0bd476a20029bd642d85. ...
- 洛谷p3803 FFT入门
洛谷p3803 FFT入门 ps:花了我一天的时间弄懂fft的原理,感觉fft的折半很神奇! 大致谈一谈FFT的基本原理: 对于两个多项式的卷积,可以O(n^2)求出来(妥妥的暴力) 显然一个多项式可 ...
- hdu1402 FFT入门
参考这里:http://www.cnblogs.com/pdev/p/4354705.html http://www.cnblogs.com/pdev/p/4354629.html 题意:求大数乘法 ...
- FFT 入门
推荐博客 :https://oi.men.ci/fft-notes/ 卷积的理解 : https://www.zhihu.com/question/22298352?rf=21686447 题目链接 ...
- bzoj2179: FFT快速傅立叶
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...
- 多项式FFT相关模板
自己码了一个模板...有点辛苦...常数十分大,小心使用 #include <iostream> #include <stdio.h> #include <math.h& ...
- 3-idiots hdu4609 母函数+FFT 组合数学题
http://acm.hdu.edu.cn/showproblem.php?pid=4609 题意:1e5个数,求取三个数能形成三角形的概率. 题解(这怎么会是fft入门题QAQ): 概率的算法就是三 ...
- 模板:快速傅里叶变换(FFT)
参考:http://blog.csdn.net/f_zyj/article/details/76037583 如果公式炸了请去我的csdn博客:http://blog.csdn.net/luyouqi ...
- HDU 1402 大数乘法 FFT、NTT
A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...
随机推荐
- Android 显示意图激活另外一个Actitity
1.跳转到一个新的Actitity 新建项目, 新建一个java类OtherScreenActivity 继承自 Activity类 package com.wuyudong.twoactivity; ...
- Android 利用内容提供者添加联系人的操作
上文中<Android 获取系统的联系人>主要介绍了怎样获取系统联系人并实战了一下,本文将介绍如何添加一条联系人信息 添加联系人 1. 添加raw_contacts表,添加一条联系人的id ...
- 【读书笔记】iOS网络-测试与操纵网络流量
一,观测网络流量. 观测网络流量的行为叫做嗅探或数据包分析. 1,嗅探硬件. 从iOS模拟器捕获数据包不需要做特别的硬件或网络配置.如果需要捕获这些数据包,那么可以使用嗅探软件来监听回送设备或是用于连 ...
- tomcat常见错误及解决方案
一,tomcat启动时错误 1:The JAVA_HOME environment variable is not defined This environment variable is neede ...
- .net C# 图片转Base64 Base64转图片
//图片 转为 base64编码的文本 private void button1_Click(object sender, EventArgs e) { OpenFileDialog dlg = ne ...
- 一个简单的Servlet容器实现
上篇写了一个简单的Java web服务器实现,只能处理一些静态资源的请求,本篇文章实现的Servlet容器基于前面的服务器做了个小改造,增加了Servlet请求的处理. 程序执行步骤 创建一个Serv ...
- Vector和Stack(已过时,不建议使用)
以下内容基于jdk1.7.0_79源码: 什么是Vector和Stack Vector:线程安全的动态数组 Stack:继承Vector,基于动态数组实现的一个线程安全的栈: Vector和Stack ...
- Linux Found a swap file by the name filename
在Linux中使用vi命令编辑mysql_backup.sh时遇到下面提示信息 E325: ATTENTION Found a swap file by the name ".mysql_b ...
- c++双字符常量
ascii表中 A是65,B是66,16706是A乘256+B 一些双字符的汉字也可以通过此方法转为int数字
- Python基础之装饰器
1.什么是装饰器? Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西.虽然 ...