上次学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的更多相关文章

  1. [学习笔记]FFT——快速傅里叶变换

    大力推荐博客: 傅里叶变换(FFT)学习笔记 一.多项式乘法: 我们要明白的是: FFT利用分治,处理多项式乘法,达到O(nlogn)的复杂度.(虽然常数大) FFT=DFT+IDFT DFT: 本质 ...

  2. FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅲ

    第三波,走起~~ FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅰ FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅱ 单位根反演 今天打多校时 1002 被卡科技了 ...

  3. [学习笔记]NTT——快速数论变换

    先要学会FFT[学习笔记]FFT——快速傅里叶变换 一.简介 FFT会爆精度.而且浮点数相乘常数比取模还大. 然后NTT横空出世了 虽然单位根是个好东西.但是,我们还有更好的东西 我们先选择一个模数, ...

  4. [学习笔记] 多项式与快速傅里叶变换(FFT)基础

    引入 可能有不少OIer都知道FFT这个神奇的算法, 通过一系列玄学的变化就可以在 $O(nlog(n))$ 的总时间复杂度内计算出两个向量的卷积, 而代码量却非常小. 博主一年半前曾经因COGS的一 ...

  5. 【学习笔记】快速傅里叶变换(FFT)

    [学习笔记]快速傅里叶变换 学习之前先看懂这个 浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理--gzy hhh开个玩笑. 讲一下\(FFT\) ...

  6. 快速傅里叶变换(FFT)学习笔记

    定义 多项式 系数表示法 设\(A(x)\)表示一个\(n-1\)次多项式,则所有项的系数组成的\(n\)维向量\((a_0,a_1,a_2,\dots,a_{n-1})\)唯一确定了这个多项式. 即 ...

  7. FFT和NTT学习笔记_基础

    FFT和NTT学习笔记 算法导论 参考(贺) http://picks.logdown.com/posts/177631-fast-fourier-transform https://blog.csd ...

  8. 「学习笔记」FFT 之优化——NTT

    目录 「学习笔记」FFT 之优化--NTT 前言 引入 快速数论变换--NTT 一些引申问题及解决方法 三模数 NTT 拆系数 FFT (MTT) 「学习笔记」FFT 之优化--NTT 前言 \(NT ...

  9. 「学习笔记」FFT 快速傅里叶变换

    目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...

随机推荐

  1. 深入理解Lambda

    概述 Lambda是一个表达式,也可以说它是一个匿名函数.然而在使用它或是阅读Lambda代码的时候,却显得并不那么容易.因为它匿名,因为它删减了一些必要的说明信息(比如方法名).下面就来说说Lamb ...

  2. Redis 应用笔记

    模糊匹配 语法:KEYS pattern 说明:返回与指定模式相匹配的所用的keys. 该命令所支持的匹配模式如下: (1)?:用于匹配单个字符.例如,h?llo可以匹配hello.hallo和hxl ...

  3. smarty语法

    HTML中直接显示数据 <{$data}> foreach循环 <{foreach from=$data item=item key=key}> <li data-ind ...

  4. git网站

    https://backlog.com/git-tutorial/cn/   ------------------------------------------------------------- ...

  5. WAF 强化学习

    参考:https://github.com/duoergun0729/3book/tree/master/code/gym-waf 代码: wafEnv.py #-*- coding:utf-8 –* ...

  6. 一个css3 DNA 效果

    这个效果就是 不断沿 Y 轴旋转 <div id="container"></div> <style> body{ background:bla ...

  7. HTTP返回结果状态码小结

    HTTP 状态码负责表示客户端 HTTP 请求的返回结果.标记服务器端的处理是否正常.通知出现的错误等工作. 一.状态码的类别 状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果.借助状态 ...

  8. android官网被封掉了,只好用这个网站进谷歌了!嘎嘎

         http://developer.android.com/sdk/index.html    这个可以进去,但是必须是搜狐 .360,uc都不用特意FQ     http://173.1 ...

  9. C++静态数据成员实现

    静态数据成员是在一个类中用关键字static声明的数据成员.在C++中,一般使用静态成员来代替C语言的全局变量,以达到数据共享.C和C++的全局变量有一定的局限性,可以任意被修改,也容易和其它的变量名 ...

  10. 余弦相似性计算及python代码实现

    A:西米喜欢健身 B:超超不爱健身,喜欢打游戏 step1:分词 A:西米/喜欢/健身 B:超超/不/喜欢/健身,喜欢/打/游戏 step2:列出两个句子的并集 西米/喜欢/健身/超超/不/打/游戏 ...