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 ...
随机推荐
- 在GitHub上管理项目
在GitHub上管理项目 新建repository 本地目录下,在命令行里新建一个代码仓库(repository) 里面只有一个README.md 命令如下: touch README.md git ...
- Android 下拉列表框、文本框、菜单
1.下拉列表框(Spinner) 项目布局 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andr ...
- Android 在不同Actitity之间数据传递
本文实现一个简易的人品计算器来实践在不同Actitity之间数据传递 intent的数据传递 从A界面打开B界面 把A界面的数据传递给B界面 1. intent.setData(uri) -- int ...
- iOS cookie问题
获取cookie时汉字应转换为UTF8格式
- js 继承
ECMAScript只支持实现继承(继承实际的方法),主要依靠原型链来实现. 1.原型链 基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 示例: function SuperType ...
- WPF学习之路(九)导航链接
Hyperlink WPF中超链接类型是Hyperlink,除了能在页面之间导航,还能再同一个页面下进行段落导航 实例: <Grid> <FlowDocumentReader> ...
- Java开发人员最常犯的10个错误
这个列表总结了10个Java开发人员最常犯的错误. Array转ArrayList 当需要把Array转成ArrayList的时候,开发人员经常这样做: List<String> list ...
- 【真的是随笔】如何利用htaccess把网站流量引到一个页面上(站点维护页效果)
咕咕咕,好久没来博客园转悠了,最近(这个最近好长啊)一直没时间写博(事实上也不知道写点什么有逼格的东西),所以一直都在潜水,,,(此处省略n字) 好了切入正题,关于如何把网站的所有流量引向一个页面的方 ...
- 小议如何使用APPLY
简介 如果你打算为在结果集中的每条记录写一个调用表值函数或者表值表达式的select语句,那么你就能用到APPLY 操作符来实现.一般又两种形式写法: 第一种格式就是CROSS APPLY.这种格式可 ...
- Java并发之ThreadPoolExecutor 线程执行服务
package com.thread.test.thread; import java.util.concurrent.ExecutorService; import java.util.concur ...