多项式计算-FFT与NTT学习小结

【写于2016-10-11,现在从hexo搬运过来】

看了两天FFT
一直没有学fft,直到10天前打beijing online contest时,碰到了一个NTT的裸题,发现必须要看看了

hexo博客配置有一些问题,mathjax公式一直不能正常加载,可以访问我在cmdmarkdown上发布的版本 here
这几天看了一些博客,发现它纯粹在数学上需要功底。FFT,NTT的题目中,简单的那部分基本可以分为两个方面:建模列出表达式+套版
理解模板中那些纯粹属于FFT,NTT的做法并不难。难的是怎么用到题目中,这需要数学功底。。

我就不在这里从头写了,网上已经有了很好的博客来讲解FFT
首先,我决定尝试叙述一下逻辑关系:
我们现在要解决的,是高效的计算多项式乘法,传统朴素的方法是 O(n^2) 的。
而FFT,即快速傅里叶变换,通过某种技巧,使复杂度降到 O(nlogn) , 但是FFT的弱点在于,它的计算在复数域内进行,因此存在精度问题。
于是有了NTT,即快速数论变换。这一变换是对FFT的改进,使得所有计算在(Z_p),即模p剩余系下考虑。因此,整数意义下的FFT问题可以用NTT来解决,从而避免精度误差。

  1. FFT

    贴出一些文章:

这两篇文章说的很清楚。傅里叶变换建立在一些基础的处理手法和引理上。
首先是多项式的点值表达,很好理解,一个n次一元多项式,与n个不同的点是相互确定的。这个可以从代数的角度上来理解。文章的推导也阐明了这点。
点值表达给我们带来的是,我们想要表述一个多项式,只需任取n个不同的点,就能唯一地表述它。我们想要求一个多项式,只要设法找到n个不同的该多项式经过的点,就可以求出其系数表达。
因此,我们接下来的工作就放在点值上来考虑。经验表明,只有真正想清楚了点值表达是什么以及它对下面的工作起到什么作用,才能更好地理解下面的推导。

教程里已经推导了,单纯的去取n个点,来作点值表达,是可以完成多项式相乘计算的。然而,普遍的时间复杂度还是O(n^2) 的。

到这时候,FFT才真正登场。
FFT的做法是,同样基于点值表达,但是选了一组很巧妙的点,即n 重单位复根
[ \omega_k = e^\frac{2k\pi}{n} , k = 0,1,2 … n-1 ]
这组点的点值之间存在着巧妙的关系,使得我们可以用分治的思路来求解所有点值,复杂度降到O(nlogn)
思想就是这些,剩下的就是引理,DFT&IDFT,Rader等等具体的一些东西了,上面的文章讲得很清楚了

  1. NTT
    多项式计算终极版(ACdreamer)
    关于gn的取法和原根的理解,这一篇说的比较好:
    原根与NTT
    正如刚才所说,FFT存在精度上的误差,有时候直接影响答案的正确输出。因此,针对整数意义上的问题,我们有NTT
    NTT是一个改进,我们选取一组新的点值
    [\omega_n=g^\frac{p-1}{n}]
    其中p为质数,n是2的方幂,且满足 n | (p-1)

上面的文章证明了,wn具有同n重单位复根一样的那几条性质,因此可以代替n重单位复根来做计算。

所以要写NTT,只需要改动取wn 的部分即可。
我们需要的常数是大质数p,和它的原根g,p一般是取费马素数的形式,即:
[p = c2^k+1 \quad c,k∈N_{+}]
常用的是p = 479
2^21+1 , 对应原根 g = 3
p并不总是随便取的,有时候题目要求你取某个指定的模数p,如果模数p是费马素数,那么不用做任何改动,求出其原根,对应的做一些更改就好。
原根的求法和更多的一些知识:这里
(这个我还没仔细看。。目前做的题还很少。。)
如果是任意的质数,听说的做法是找两个小的费马素数来分别算,然后再用crt来合并,好吧我还没做这类题。。

目前想得到的一些自己对FFT,NTT的理解就是这样了,代码中有些细节的地方还需要我仔细琢磨,并没有搞得很透彻。

几道题目:
FFT入门题:
hdu1402
大数乘法,n位的大整数,实际上可以看成n次的多项式。两个大数的相乘,就可以理解成这两个多项式的乘法。在用FFT计算好乘法之外,我们额外做的就是进位和顺序调整了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <utility>
#include <string>
#include <queue>
//#define maxn 1024
#define LL long long
#define fp freopen("in.txt","r",stdin)
#define fpo freopen("out9.txt","w",stdout)
//#define judge
using namespace std;
const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 250050;
const int INF = 0x3f3f3f3f;
const double lg2 = log(2.0);
const LL MOD = 1e9+7;
#define MAX 5050
struct Complex
{
    double re,im;
    Complex(double re = 0.0, double im = 0.0)
    {
        this->re &#
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值