目录
转载请注明
1.前言
2.前提知识
2.1秦九韶算法
2.1.1介绍
2.1.2算法的具体过程
2.1.3 CODE
2.2复数
2.2.1虚数
2.2.2复数及其属性
2.2.3复数的运算法则
2.2.4一种特殊的复数
2.2.5欧拉公式
2.3多项式的两种表示方式
2.3.1系数向量法
2.3.2点值法
2.3.3两者之间的联系
2.4单位根
2.4.1基本概念
2.4.2性质
3.FFT的计算过程概要
4.DFT(离散傅里叶变换)
4.1单位复数根的点值法表示多项式
4.2 DFT()
4.3 DFT的两种求法
4.3.1递归分治法(自顶向下,常数比较大)
4.3.2迭代法(自底向上,常数比较小)
5. IDFT(逆离散傅里叶变换)
5.1 IDFT与逆矩阵
5.2 伪代码
6.卷积定理
7.快速数论变换
7.1原根
7.2 常用模数和对应原根的表
8.参考文献
我计划将分成两章讲解“快速傅里叶变换与快速数论变换”。这一篇博客是我在研究和学习了3~4天的FFT和FNT的基础之上诞生的。
本片博客主要讲解FFT和FNT的最基本的知识。
FFT和FNT是用来解决数个多项式相乘的问题的。
秦九韶算法(在西方叫做“霍纳法则”)能在 的时间内计算 次多项式的值
该多项式可以表示为:
double f (int n,double a[],double x){//n代表a[]的大小,数组的传递需要指定数组大小
int i; //a[]储存每个x项的系数值,在函数外初始化
double p=a[n];//初始化p;
for(i=n;i>0;i--)
p=a[i-1]+x*p;
return p;
}
设
则 ,此方程在实数集上没有解。于是,我们再设置一个复数集。
在复数集上,我们设 ,于是此方程也有解。其中,我们把 叫做虚数
我们把实数+虚数结合体叫做复数,比如:
其中, 是实部, 是虚部。
设复数 ,则 的模长为 ,辐角为
设 ,
则
则模长为 ,辐角为
因此,有结论:两个复数相乘:模长相乘,辐角相加
一种特殊的复数:, 它的模长是 , 辐角为 。
这个复数可以用来旋转一个复数.
比如,设 , 再设
由上面的结论,我们得到:,
即复数 顺时针旋转了
令上面的 ,得到
这就是著名的欧拉公式。
设两个 次多项式分别为 ,
该多项式在数学上有两种表示方式,分别为:系数向量法、点值法
注意,在这里,我们用任意一个变量取代 ,也就是把上面这些式子当作形式幂级数(形式幂级数的概念在母函数中提到过)
所谓的系数向量法就是利用多项式中未知数前的系数构成的一组系数向量(我们将它看成是列向量)
即 和
若采用系数向量法来表示两个多项式相乘,
则有
时间复杂度为
所谓的点值法就是用 个 点值对表示该多项式,每一个点值对由 和 构成
即
点值法的本质就是取 个样本,从而构成一个 阶方阵。然后就可以唯一确定一组系数向量,从而唯一确定一个多项式
系数法 点值法 “求值”运算
点值法 系数法 “插值”运算
设 为一个复数,令 ,我们称其为 次单位复数根(就是模长为 的复数的 次等于 的复数集)
有以下性质:
性质一:复数集的基数是 ,也就是有 个这样的复数
性质二:复数集中的复数均匀分布在复平面上的单位圆上,将单位圆均匀的分成了 个部分
性质三:复数集中的各个复数的模长为
性质四:复数集中的元素的形式为 ,其中 。我们把 叫做主 次单位复数根
性质五:由性质四,得到复数集中各元素分别为:
性质五:复数集是一个群,满足以下恒等式
一、
二、(消去引理)
三、(由恒等式二得到本推论)
四、
五、,其中, 是一个常数
六、 (折半引理)
备注:折半引理是FFT得以成立的充要条件
(图片摘自图片原文地址)
STEP 1:将系数向量A,B转换为点值法 ------------------
STEP 2:将A,B两个点值相乘 ---------------------------------
STEP 3:将乘好以后的点值转换为新的系数向量C------
因此,可以得到 FFT 的时间复杂度为
我们将STEP 1 叫做 DFT(即离散傅里叶变换),将STEP 2 叫做 IDFT(即逆离散傅里叶变换)
STEP 1 和 STEP 2互为逆运算
接下来,我将分成了两个部分(DFT和IDFT)来讲解FFT是怎么算的
不过,在此之前,约定下文中的 是 的幂次(即 ,不到 ,就用 补齐)
是将系数向量转化为点值,是求值运算
设多项式为
设多项式的系数向量 为 (已知)
设用点值法表示为
接下来,用 次单位复数根中的 个复数和其对应的多项式的值,分别代替点值法中的 和
从而得到一组新的点值式:,记作 (未知)
则通过 运算:= , 得到 的离散傅里叶变换值
设
再设 ,
那么就有
由 2.4.2 的消去引理得到: ------------------------
由 2.4.2 的折半引理得到:-----------------
观察 和 ,我们得到一个结论:每次计算一个 之后,就能直接计算得到
根据这个结论,就能使得原问题的规模缩小到原来的一半,因此, 的时间复杂度就是
讲的通俗一点就是:
以下是伪代码:
():
以 为例子,观察下图中系数向量在分治法中的变化:
(图片摘自图片原文地址)
STEP 1 : 是全序排列
STEP 2 : 末位为0的在左树,末位为1的在右树
STEP 3 : 倒数第二位为0的在左树,倒数第二位为1的在右树
STEP 4 : 倒数第三位为0的在左树,倒数第三位为1的在右树
考虑用迭代的方法计算 ,那么只要知道 的系数向量的顺序就可以自底向上迭代得到
再观察 STEP 4 中个系数的顺序,我们发现:
0 | 8 | 4 | 12 | 2 | 10 | 6 | 14 | 1 | 9 | 5 | 13 | 3 | 11 | 7 | 15 |
0000 | 1000 | 0100 | 1100 | 0010 | 1010 | 0110 | 1110 | 0001 | 1001 | 0101 | 1101 | 0011 | 1011 | 0111 | 1111 |
0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
将 STEP 4 中个系数的二进制翻转后的数是逐一递增的。(我们把这个叫做逆序置换)
我们假设 是 将 的二进制翻转之后的数,那么我们就可以得到以下的伪代码:
()
()
()
(逆序置换)的具体代码:
//采用DP
int rev[N];
void reverse(int n)
{
memset(rev,0,sizeof(rev));
for(int i=0;i>1)|((i&1)<<(n-1));//(i&1)表示当前的项的奇偶,rev[i]的右n-1位与rev[i/2]的右n-1位相同
}
是将点值转化为系数向量,是插值运算
设多项式为
设多项式的系数向量 为 (未知)
通过 得到一组点值 ,记作 (已知)
则有 = -----------
计算 ,本质上就是解下列方程组:
写成矩阵的形式就是:
(以上图片截自图片原文地址)
这样, 就相当于把 过程中的 换成 ,然后做一次 ,之后结果除以 就可以了。
把上面两种 伪代码中的 换成 , 换成 即可。最后,每一项元素除以 。
设 是两个 次( 是 的幂次,不到 就用 补齐)多项式,那么有:
( 表示卷积)
注意:这里不是 ,而是 !因为 这两个 次多项式相乘的话,就会多产生 项
比如:
-------------------------------------------------
--------------------------------------------------
那么 -------
观察得到, 比 或 多了 项,所以要把原来的 扩展为
我们发现,使用 次单位复数根可以简化多项式的计算,但是复数的计算难免会产生精度的误差。在对大整数的多项式的计算过程中就会失去优势,因此,我们将采用一种新的方式来代替了原来的 次单位复数根。
设一个素数为 ,根据费马大定理有:
若某个数 是 的原根,则有
根据原根的特点,我们设一个素数 ,
设 (对应主 次单位复数根)
有 (对应 次单位复数根性质一)
有 互不相同 互不相同(对应 次单位复数根性质二)
有 , 于是 ,
又因为和,所以 (对应 次单位复数根性质三)
有了上面三条 一 一对应的性质,我们就可以使用这样的 对应的 代替 次单位复数根。
r⋅2k+1 | r | k | g |
---|---|---|---|
3 | 1 | 1 | 2 |
5 | 1 | 2 | 2 |
17 | 1 | 4 | 3 |
97 | 3 | 5 | 5 |
193 | 3 | 6 | 5 |
257 | 1 | 8 | 3 |
7681 | 15 | 9 | 17 |
12289 | 3 | 12 | 11 |
40961 | 5 | 13 | 3 |
65537 | 1 | 16 | 3 |
786433 | 3 | 18 | 10 |
5767169 | 11 | 19 | 3 |
7340033 | 7 | 20 | 3 |
23068673 | 11 | 21 | 3 |
104857601 | 25 | 22 | 3 |
167772161 | 5 | 25 | 3 |
469762049 | 7 | 26 | 3 |
998244353 | 119 | 23 | 3 |
1004535809 | 479 | 21 | 3 |
2013265921 | 15 | 27 | 31 |
2281701377 | 17 | 27 | 3 |
3221225473 | 3 | 30 | 5 |
75161927681 | 35 | 31 | 3 |
77309411329 | 9 | 33 | 7 |
206158430209 | 3 | 36 | 22 |
2061584302081 | 15 | 37 | 7 |
2748779069441 | 5 | 39 | 3 |
6597069766657 | 3 | 41 | 5 |
39582418599937 | 9 | 42 | 5 |
79164837199873 | 9 | 43 | 5 |
263882790666241 | 15 | 44 | 7 |
1231453023109121 | 35 | 45 | 3 |
1337006139375617 | 19 | 46 | 3 |
3799912185593857 | 27 | 47 | 5 |
4222124650659841 | 15 | 48 | 19 |
7881299347898369 | 7 | 50 | 6 |
31525197391593473 | 7 | 52 | 3 |
180143985094819841 | 5 | 55 | 6 |
1945555039024054273 | 27 | 56 | 5 |
4179340454199820289 | 29 | 57 | 3 |
(图片摘自图片原文地址)
1.http://blog.miskcoo.com/2015/04/polynomial-multiplication-and-fast-fourier-transform#i-12
2.http://blog.miskcoo.com/2014/07/fft-prime-table
3.https://www.cnblogs.com/rvalue/p/10120174.html#fft%E7%9A%84%E9%80%9F%E5%BA%A6%E4%BC%98%E5%8C%96%E4%B8%8E%E8%BF%AD%E4%BB%A3%E5%AE%9E%E7%8E%B0
4.算法导论