多项式算法2:NTT(快速数论变换)

  • 前言
  • 前置知识
  • 正文

前言

算法简介
上次的FFT大量使用浮点数,有精度不好控制的问题,使用可以保证精度的方法又容易超时。这里NTT是在取模的一个前提下找到一个新的且为整数的单位根(在取模意义下),我们记其为GGG,那么用这个根去替代原来的ωn\omega_nωn​来进行运算。因为所有运算就会变成整数运算,所以精度误差大大降低。使用NTT也方便按照题目要求进行取模运算,缺点就是只能进行以整数为系数的多项式运算,同时数组长度和每一项的值(如果题目原本不要求取模的话)都有限制。

前置知识


若正整数aaa,ppp互质且满足p>1p>1p>1,则:
对于an≡1(modp)a^n \equiv 1(\mod p )an≡1(modp)最小的nnn,我们称之为aaa模ppp的阶,记作δp(a)\delta_p(a)δp​(a)。
对于i∈[0,δp(a))i \in [0,\delta_p(a))i∈[0,δp​(a)),所有的aimodpa^i \mod paimodp结果互不相同。
我们假设存在两个整数jjj,kkk满足aj≡ak(modp),0≤j<k<δp(a)a^j \equiv a^k (\mod p),0 \le j \lt k \lt \delta_p(a)aj≡ak(modp),0≤j<k<δp​(a)则有ak−j≡1(modp)a^{k-j}\equiv 1 (\mod p)ak−j≡1(modp),与阶的定义矛盾。
原根
设nnn是正整数,ggg是整数,若ggg模nnn的阶δn(g)=φ(n)\delta_n(g)= \varphi(n)δn​(g)=φ(n)且gcd⁡(g,n)=1\gcd(g,n) =1gcd(g,n)=1,则称ggg为nnn的一个原根。
若gcd⁡(g,n)=1\gcd(g,n) =1gcd(g,n)=1且n>0n>0n>0,则ggg为nnn的一个原根的充要条件为:S={g1,g2,g3,⋯,gφ(n)}S=\{g^1,g^2,g^3,\cdots,g^{\varphi(n)} \}S={g1,g2,g3,⋯,gφ(n)}为nnn的一组简化剩余系(即一组包含所有模nnn后与nnn互质且取模后的数两两不相同的数)。
由上文可知若ggg为原根,则SSS中任意两个元素在模nnn的意义下不同余。
又因为gcd⁡(g,n)=1\gcd(g,n)=1gcd(g,n)=1,故而SSS为nnn的一组简化剩余系。
FFT
FFT详解

正文

关于原根和模数的选择
由于NTT大量二分数组,我们希望选一些形如p=q×2k+1p=q \times 2^k + 1p=q×2k+1的质数ppp,其中qqq为奇自然数,kkk为整数。
我们有以下的原根可以选。

模数 原根 原根的逆元 最大长度
469762049=7×226+1469762049=7 \times 2^{26} + 1469762049=7×226+1 3 156587350 2262^{26}226
998244353=119×223+1998244353=119 \times 2^{23} + 1998244353=119×223+1 3 332748118 2232^{23}223
2281701377=17×227+12281701377=17 \times 2^{27} + 12281701377=17×227+1 3 760567126 2272^{27}227
1004535809=479×221+11004535809=479 \times 2^{21} + 11004535809=479×221+1 3 334845270 2212^{21}221

DFT
通过观察,我们可以发现原根有一些和复数单位根很像的性质:
我们令nnn为大于111的222的幂,ppp为素数且n∣(p−1)n |(p-1)n∣(p−1),ggg为ppp的一个原根。
我们设gn=gp−1ng_n=g^{\frac{p-1}{n}}gn​=gnp−1​所以gnn=gn×p−1n=gp−1g_n^n=g^{n \times \frac{p-1}{n}}=g^{p-1}gnn​=gn×np−1​=gp−1gnn2=gp−12g_n^{\frac{n}{2}}=g^{\frac{p-1}{2}}gn2n​​=g2p−1​ganak=gak(p−1)an=gk(p−1)n=gnkg_{an}^{ak}=g^{\frac{ak(p-1)}{an}}=g^{\frac{k(p-1)}{n}}=g_n^kganak​=ganak(p−1)​=gnk(p−1)​=gnk​通过观察不难得出:gnn≡gp−1≡1(modp)g_n^n \equiv g^{p-1} \equiv 1 (\mod p)gnn​≡gp−1≡1(modp)又因为(gnn2)2≡1(modp)(g_n^{\frac{n}{2}})^2\equiv 1 (\mod p)(gn2n​​)2≡1(modp),且同属于一个简化剩余系,不能相等,
故gnn2≡−1(modp)g_n^{\frac{n}{2}}\equiv -1 (\mod p)gn2n​​≡−1(modp)同时我们有(gnk+n2)2≡(gnk)2×gnn≡(gnk)2(modp)(g_n^{k + \frac{n}{2}})^2\equiv (g_n^k)^2 \times g_n^n \equiv (g_n^{k})^2 (\mod p)(gnk+2n​​)2≡(gnk​)2×gnn​≡(gnk​)2(modp)又因为同属于一个简化剩余系,不能相等,可得gnk+n2≡−gnk(modp)g_n^{k + \frac{n}{2}} \equiv -g_n^{k} (\mod p)gnk+2n​​≡−gnk​(modp)
总而言之,单位根在FFT中用到的性质原根都有。
gnn≡1(modp)g_n^n \equiv 1 (\mod p)gnn​≡1(modp)
ganak≡gnk(modp)g_{an}^{ak} \equiv g_n^k(\mod p)ganak​≡gnk​(modp)
gnn2≡−1(modp)g_n^{\frac{n}{2}}\equiv -1 (\mod p)gn2n​​≡−1(modp)
gnk+n2≡−gnk(modp)g_n^{k + \frac{n}{2}} \equiv -g_n^{k} (\mod p)gnk+2n​​≡−gnk​(modp)
所以在DFT的过程中带入gnkg_n^kgnk​和gnk+n2g_n^{k+\frac{n}{2}}gnk+2n​​实质上和带入ωnk\omega_n^kωnk​和ωnk+n2\omega_n^{k+\frac{n}{2}}ωnk+2n​​一样。
IDFT
和FFT一样,用矩阵来辅助思考:
[gn0gn0gn0⋯gn0gn0gn1gn2⋯gnn−1gn0gn2gn4⋯gn2n−2gn0gn3gn6⋯gn3n−3⋮⋮⋮⋱⋮gn0gnn−1gn2n−2⋯gn(n−1)2]∗[a0a1a2a3⋮an−1]=[F0F1F2F3⋮Fn−1]\left[ \begin{array}{ccccc} g_{n}^{0} &g_{n}^{0}&g_{n}^{0} & \cdots & g_{n}^{0} \\ g_{n}^{0}&g_{n}^{1}&g_{n}^{2} & \cdots & g_{n}^{n-1} \\ g_{n}^{0} & g_{n}^{2} & g_{n}^{4} & \cdots &g_{n}^{2n-2} \\ g_{n}^{0} &g_{n}^{3} & g_{n}^{6} &\cdots & g_{n}^{3n-3} \\ \vdots & \vdots & \vdots &\ddots & \vdots \\ g_{n}^{0} &g_{n}^{n-1} &g_{n}^{2n-2} &\cdots &g_{n}^{(n-1)^2}\\ \end{array} \right] * \left[ \begin{array}{ccccc} a_0\\ a_1\\ a_2\\ a_3\\ \vdots\\ a_{n-1}\\ \end{array} \right] = \left[ \begin{array}{ccccc} F_0\\ F_1\\ F_2\\ F_3\\ \vdots\\ F_{n-1}\\ \end{array} \right] ⎣⎢⎢⎢⎢⎢⎢⎢⎡​gn0​gn0​gn0​gn0​⋮gn0​​gn0​gn1​gn2​gn3​⋮gnn−1​​gn0​gn2​gn4​gn6​⋮gn2n−2​​⋯⋯⋯⋯⋱⋯​gn0​gnn−1​gn2n−2​gn3n−3​⋮gn(n−1)2​​⎦⎥⎥⎥⎥⎥⎥⎥⎤​∗⎣⎢⎢⎢⎢⎢⎢⎢⎡​a0​a1​a2​a3​⋮an−1​​⎦⎥⎥⎥⎥⎥⎥⎥⎤​=⎣⎢⎢⎢⎢⎢⎢⎢⎡​F0​F1​F2​F3​⋮Fn−1​​⎦⎥⎥⎥⎥⎥⎥⎥⎤​
和FFT不太一样,乘以单位根的共轭复数的过程将变成乘以原根在模ppp意义下的逆元,最后再乘以长度nnn模ppp下的逆元即可。
证明如下(以下运算均在模ppp的条件下进行):
gnk=g(p−1n)kg_n^k=g^{(\frac{p-1}{n})k}gnk​=g(np−1​)k
如上矩阵,F(k)=∑j=0n−1a(j)(gni)jkF(k)=\sum_{j=0}^{n-1}a(j) (g_n^{ i })^{jk}F(k)=j=0∑n−1​a(j)(gni​)jk
逆变换为:
1n∑k=0n−1F(k)(gni)−jk=1n∑k=0n−1(∑l=0n−1a(l)(gni)lk)(gni)−jk=1n∑k=0n−1(∑l=0n−1a(l)(gni)(l−j)k)\frac{1}{n} \sum_{k=0}^{n-1}F(k) (g_n^{ i })^{ -jk }=\frac{1}{n} \sum_{k=0}^{n-1}( \sum_{l=0}^{n-1}a(l)(g_n^{ i })^{ lk } )(g_n^{ i })^{- jk }=\frac{1}{n} \sum_{k=0}^{n-1}( \sum_{l=0}^{n-1}a(l)(g_n^{ i })^{ (l-j)k } )n1​k=0∑n−1​F(k)(gni​)−jk=n1​k=0∑n−1​(l=0∑n−1​a(l)(gni​)lk)(gni​)−jk=n1​k=0∑n−1​(l=0∑n−1​a(l)(gni​)(l−j)k)
式子=1n∑k=0n−1(a(0)(gni)(0−j)k+a(1)(gni)(1−j)k+⋯+a(n−1)(gni)((n−1)−j)k)=\frac{1}{n} \sum_{k=0}^{n-1}( a(0)(g_n^{ i })^{ (0-j)k} + a(1)(g_n^{ i })^{ (1-j)k} + \cdots + a(n-1)(g_n^{ i })^{ ((n - 1)-j)k} )=n1​k=0∑n−1​(a(0)(gni​)(0−j)k+a(1)(gni​)(1−j)k+⋯+a(n−1)(gni​)((n−1)−j)k)
展开kkk的求和式得
=1n[a(0)((gni)(0−j)0+(gni)(0−j)1+⋯+(gni)(0−j)(n−1))+a(1)((gni)(1−j)0+(gni)(1−j)1+⋯+(gni)(1−j)(n−1))+⋯+a(n−1)((gni)((n−1)−j)0+(gni)((n−1)−j)1+⋯+(gni)((n−1)−j)(n−1))]=\frac{1}{n}[ a(0)((g_n^{ i })^{ (0-j)0 }+(g_n^{ i })^{ (0-j)1 } + \cdots + (g_n^{ i })^{ (0-j)(n-1) }) + \\ a(1)((g_n^{ i })^{ (1-j)0 }+(g_n^{ i })^{ (1-j)1 } + \cdots + (g_n^{ i })^{ (1-j)(n-1) }) + \\ \cdots + \\ a(n-1)((g_n^{ i })^{ ((n-1)-j)0 }+(g_n^{ i })^{ ((n-1)-j)1 } + \cdots + (g_n^{ i })^{ ((n-1)-j)(n-1) }) ]=n1​[a(0)((gni​)(0−j)0+(gni​)(0−j)1+⋯+(gni​)(0−j)(n−1))+a(1)((gni​)(1−j)0+(gni​)(1−j)1+⋯+(gni​)(1−j)(n−1))+⋯+a(n−1)((gni​)((n−1)−j)0+(gni​)((n−1)−j)1+⋯+(gni​)((n−1)−j)(n−1))]
由等比数列的求和公式得:
a(x)((gni)(x−j)0+(gni)(x−j)1+⋯+(gni)(x−j)(n−1))=a(x)1−((gni)n(x−j))n1−(gni)(x−j)a(x)((g_n^{ i })^{ (x-j)0 }+(g_n^{ i })^{ (x-j)1 } + \cdots + (g_n^{ i })^{ (x-j)(n-1) })=a(x) \frac{1- ((g_n^{ i })^{{n}(x-j)})^n}{1- (g_n^{ i })^{(x-j)} }a(x)((gni​)(x−j)0+(gni​)(x−j)1+⋯+(gni​)(x−j)(n−1))=a(x)1−(gni​)(x−j)1−((gni​)n(x−j))n​当j≠xj \neq xj​=x,可得:a(x)1−((gni)(x−j))n1−(gni)(x−j)=a(x)1−(gi)(x−j)1−(gni)(x−j)=a(x)1−1(x−j)1−(gni)(x−j)=0a(x) \frac{1- ((g_n^{ i })^{(x-j)})^n}{1- (g_n^{ i })^{(x-j)} }=a(x) \frac{1- (g^{ i })^{(x-j)} }{1- (g_n^{ i })^{(x-j)} }=a(x) \frac{1- 1^{ (x-j)}}{1- (g_n^{ i })^{(x-j)} }=0a(x)1−(gni​)(x−j)1−((gni​)(x−j))n​=a(x)1−(gni​)(x−j)1−(gi)(x−j)​=a(x)1−(gni​)(x−j)1−1(x−j)​=0
当j=xj = xj=x,可得:a(x)((gni)(x−j)0+(gni)(x−j)1+⋯+(gni)(x−j)(n−1))=na(x)a(x)((g_n^{ i })^{ (x-j)0 }+(g_n^{ i })^{ (x-j)1 } + \cdots + (g_n^{ i })^{ (x-j)(n-1) })=na(x)a(x)((gni​)(x−j)0+(gni​)(x−j)1+⋯+(gni​)(x−j)(n−1))=na(x)
因而:1n∑k=0n−1F(k)(gni)−jk=1n∑k=0n−1(∑l=0n−1a(l)(gni)lk)(gni)−jk=na(x)\frac{1}{n} \sum_{k=0}^{n-1}F(k) (g_n^{ i })^{ -jk }=\frac{1}{n} \sum_{k=0}^{n-1}( \sum_{l=0}^{n-1}a(l)(g_n^{ i })^{ lk } )(g_n^{ i })^{- jk }=na(x)n1​k=0∑n−1​F(k)(gni​)−jk=n1​k=0∑n−1​(l=0∑n−1​a(l)(gni​)lk)(gni​)−jk=na(x)
该矩阵的逆矩阵得证。
模板题
贴上DFT与IDFT结合起来的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 1 << 22;
const int g = 3 , gi = 332748118 , mod = 998244353;//不加const时间翻四倍
ll qw( 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 rev[N];
int n , m , l;
ll a[N] , b[N];
void pre( int bit ) {for ( int i = 0 ; i < ( 1 << bit ) ; ++i ) {rev[i] = (rev[i>>1]>>1)|((i&1)<<(bit - 1));}
}
void NTT( ll *F , int len , int on ) {for ( int i = 0 ; i < len ; ++i ) {//把递归的底层交换好if ( i < rev[i] ) {swap( F[i] , F[rev[i]] );}}for ( int i = 2 ; i <= len ; i <<= 1 ) {//枚举步长,从递归的下面往上走 ll gn = qw( on ? g : gi , ( mod - 1 ) / ( i ) );for ( int j = 0 ; j <= len - 1 ; j += i ) {//走一遍步长 ll gg = 1;for ( int k = j ; k < j + i / 2 ; ++k ) {//枚举每块区间内的每一个元素ll u = F[k];ll t = gg * F[k + i / 2] % mod;F[k] = (u + t) % mod;F[k + i / 2] = ( u - t  + mod ) % mod;gg = gg * gn % mod;}}}return;
}int main () {int len = 0;scanf("%d%d",&n,&m);for ( int i = 0 ; i <= n ; ++i ) {scanf("%lld",a + i);}for ( int i = 0 ; i <= m ; ++i ) {scanf("%lld",b + i);}len = n + m;int tim = 1;l = 0;while( tim <= len ) {tim <<= 1;l++;}len = tim;pre(l);NTT( a , len , 1 );NTT( b , len , 1 );for ( int i = 0 ; i <= len - 1 ; ++i ) {a[i] = a[i] * b[i] % mod;}NTT( a , len , 0 );ll inv = qw( len , mod - 2 );for ( int i = 0 ; i <= n + m ; ++i ) {printf("%lld ",a[i] * inv % mod);}return 0;
}

任意模数的坑以后再填吧QWQ。

多项式算法2:NTT(快速数论变换)相关推荐

  1. FFT 快速傅里叶变换 NTT 快速数论变换

    SDNU 1531 a*b III (FFT模板) Description 计算a乘b,多组输入(50组以内). Input 输入a b,数据范围0 <= a,b <= 10^100000 ...

  2. 数学推导题,NTT,快速数论变换,Wannafly-乒乓球

    乒乓球 题目描述 小 BoBoBo 是某省乒乓球名列前茅的选手,现在他有 nnn 颗乒乓球一字排开,第iii颗乒乓球的权值为 wiw_iwi​ 每次他会随机从现有的乒乓球中等概率选一颗拿走,然后得到的 ...

  3. 数学推导题,NTT,快速数论变换,Wannafly-导数卷积

    导数卷积 题目描述 题解 参考了一下标程的推导过程,因为这个推导对我这种数学弱渣真的有点难鸭. [1]f(x)f(x)f(x)的iii次导函数: f(i)(x)=ai∗i!0!+ai+1∗(i+1)! ...

  4. A*B NTT快速数论变换

    wmq的A×B Problem 发布时间: 2017年4月9日 17:06   最后更新: 2017年4月9日 17:07   时间限制: 3000ms   内存限制: 512M 描述 这是一个非常简 ...

  5. NTT(快速数论变换)模板

    NTT好文 NTT快速数论变换,在acm中用于解决一类多项式卷积问题. 多项式A和多项式B求卷积后的多项式为C. 显然,如果A是n次多项式,B有m次多项式,这两个多项式都不缺项的话,乘积应为n+m次多 ...

  6. 算法学习FFT系列(2):快速数论变换NTT bzoj3992: [SDOI2015]序列统计例题详解

    bzoj3992: [SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...

  7. 【学习笔记】超简单的快速数论变换(NTT)(FFT的优化)(含全套证明)

    整理的算法模板合集: ACM模板 目录 一.前置知识 二.快速数论变换(NTT) 三.NTT证明(和FFT的关系) 四.NTT模板 数组形式的实现 vector形式的实现 点我看多项式全家桶(●^◡_ ...

  8. 比FFT还容易明白的NTT(快速数论变换)

    NTT相关 一种快速数论变换算法,这种算法是以数论为基础,对样本点为的数论变换,按时间抽取的方法,得到一组等价的迭代方程,有效高速简化了方程中的计算公式·与直接计算相比,大大减少了运算次数.(见快速傅 ...

  9. python蝴蝶代码_快速数论变换(NTT)及蝴蝶操作构造详解

    快速数论变换 本文不会从头开始介绍NTT算法,所以需要先了解FFT:永远在你身后:快速傅里叶变换(FFT)求解多项式乘法​zhuanlan.zhihu.com 除此之外,先简单的铺垫一些数论的概念同余 ...

最新文章

  1. 果园种树java_Java版淘金果园系统
  2. 如何在64位的windows平台上安装需要c编译的python扩展库
  3. js数组去重,合集等操作
  4. .net core实践系列之短信服务-架构优化
  5. 开箱即用的安全方案:MaxCompute数据安全方案介绍
  6. Maven学习(八)-----Maven依赖机制
  7. Maven(3)---Maven POM
  8. mybatis-plus使用 generator 代码生成器生成实体类支持Swagger2
  9. Leetcode每日一题:724.Find Pivot Index(寻找中心索引)
  10. 张敬富审计百度云资源_钟平逻辑英语资源百度云
  11. Unique Email Addresses
  12. nlp基础—10.结巴分词的应用及底层原理剖析
  13. Android 自定义View(三)
  14. maven profiles配置_nexus3搭建maven私服(完整版)
  15. 阿里云视频点播(上传视频)服务最新版本使用方法(解决部分依赖无法下载或不存在问题)
  16. kubernetes增加删除master节点操作
  17. chmod -R 777使用.
  18. 8-四平方和定理(拉格朗日定理)
  19. JZ2440:yaffs2 格式根文件系统制作
  20. Erphplogin Pro 连接QQ/微博/微信登录/弹窗登录 WordPress插件

热门文章

  1. 关于如何使用打码平台识别验证码
  2. Centos7 安装 ftp服务器 --失败了 妈蛋的
  3. python egg_python egg 简介
  4. VUE父组件向子组件传递数据
  5. python中wb什么意思,使用Python,“ wb”在此代码中是什么意思?
  6. 【洛谷5069】纵使日薄西山【set】【树状数组】
  7. 在线代码离线翻译Chrome插件一马v0.0.8 2018-10-31
  8. 贸然用string比较的后果
  9. 微信小程序实现直播间点赞飘心效果的示例代码
  10. Oracle database oracle12c 完全卸载 一键卸载