NTT数论变换
数论变换NTT
前置知识
- FFT:NTT的思想和FFT一样([FFT介绍](%3Ca href="https://www.cnblogs.com/guoshaoyang/p/10957854.html"%3Ehttps://www.cnblogs.com/guoshaoyang/p/10957854.html%3C/a%3E))
概述
数论变换,即NTT(Number Theory Transformation?),是基于数论域的FFT,一般我们默认FFT为负数域上的快速傅里叶变换,和NTT区分。
我们知道,FFT是利用单位复根的周期性,以\(\Theta(N \log N)\)的复杂度计算\(N\)组多项式的值。NTT其实就是在数论域上的FFT,它利用素数取模的周期性,达到了和FFT一样的效果。
原根
若\(a\)模\(P\)的阶等于\(φ(P)\),则称\(a\)为模\(P\)的一个原根(这个定义不重要)。
对于质数\(P\),若\(g\)是\(P\)的原根,那么\(g^i \mod P\)的结果两两不同;或者说\(g^t = 1 (\mod P)\)当且仅当指数为\(t=P-1\)的时候成立。
我们发现,原根的性质和单位负数根的性质一一对应,都满足周期性和周期内互异性,故可以用来代入和插值。
NTT
我们有离散傅里叶变换公式
\[X_k=\sum^{N-1}_{n=0}{x_ne^{-\frac{2\pi i}{N}nk}} \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]
和逆变换公式
\[X_k=\frac{1}{N}\sum^{N-1}_{n=0}{x_ne^{\frac{2\pi i}{N}nk}} \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]
我们只需要用原根\(g^{\frac{P-1}{N}} (\mod P)\)替代单位复根\(e^{-\frac{-2\pi i}{N}}\),就可以得到
\[X_k=\sum^{N-1}_{n=0}{x_ng^{\frac{P-1}{N}nk}}(\mod P) \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]
和
\[X_k=\frac{1}{N}\sum^{N-1}_{n=0}{x_ng^{-\frac{P-1}{N}nk}}(\mod P) \ \ \ \ \ \ \ \ \ \ \ \ (k=0,1,...,N-1)
\]
实现
和FFT的模板几乎一样,用原根\(g^{\frac{P-1}{N}} (\mod P)\)替代单位复根\(e^{-\frac{-2\pi i}{N}}\)即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=3e6+10/*Min:2^20+10*/;
const LL BASE=3,MOD=998244353,INV=332748118;
inline LL fpm(LL base,LL p){
LL ret=1;
while(p){
if(p&1)
ret=ret*base%MOD;
base=base*base%MOD;
p>>=1;
}
return ret%MOD;
}
int N,M,limit=1,lg,rev[MAXN];
inline void NTT(LL *a,int type){
for(int i=0;i<limit;i++)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int mid=1;mid<limit;mid<<=1){
int len=mid<<1/*n*/;
LL Wn=fpm(type==1?BASE:INV/*BASE^type*/,(MOD-1)/len);
for(int j=0;j<limit;j+=len){
LL w=1;
for(int k=0;k<mid;k++){
int x=a[j+k],y=w*a[j+k+mid]%MOD;
a[j+k]=(x+y)%MOD;
a[j+k+mid]=(x-y+MOD)%MOD;
w=w*Wn%MOD;
}
}
}
}
LL a[MAXN],b[MAXN];
int main(){
scanf("%d%d",&N,&M);
for(int i=0;i<=N;i++){
scanf("%lld",a+i);
a[i]=(a[i]+MOD)%MOD;
}
for(int i=0;i<=M;i++){
scanf("%lld",b+i);
b[i]=(b[i]+MOD)%MOD;
}
while(limit<=N+M){
limit<<=1;
lg++;
}
for(int i=0;i<limit;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
NTT(a,1);
NTT(b,1);
for(int i=0;i<limit;i++)
a[i]=a[i]*b[i]%MOD;
NTT(a,-1);
LL limit_inv=fpm(limit,MOD-2);
for(int i=0;i<=N+M;i++)
printf("%lld ",a[i]*limit_inv%MOD);
return 0;
}
复杂度
时间
和FFT一样,是\(\Theta(N \log N)\),在数据较小的情况下比FFT快,但因为其中有快速幂运算,如果数字太大则会导致TLE
空间
和FFT一样,是\(\Theta(N)\),但注意数组大小不是\(N+M\),而是不小于\(N+M\)的最小的\(2\)的幂
范围(素数选取)
我们需要一个大素数来取模,这个素数要大于最终输出的答案(但如果太大,就需要使用快速乘)
下面给出常用的几个素数,原根及原根的逆元
| 素数 | 原根 | 逆元 |
|---|---|---|
| 65537 | 3 | 21846 |
| 786433 | 10 | 235930 |
| 5767169 | 3 | 1922390 |
| 7340033 | 3 | 2446678 |
| 23068673 | 3 | 7689558 |
| 104857601 | 3 | 34952534 |
| 167772161 | 3 | 55924054 |
| 469762049 | 3 | 156587350 |
| 998244353 | 3 | 332748118 |
| 1004535809 | 3 | 334845270 |
| 2013265921 | 31 | 64944062 |
| 2281701377 | 3 | 760567126 |
| 3221225473 | 5 | 1932735284 |
| 75161927681 | 3 | 25053975894 |
| 77309411329 | 7 | 22088403237 |
| 206158430209 | 22 | 84337539631 |
| 2061584302081 | 7 | 589024086309 |
| 2748779069441 | 3 | 916259689814 |
| 6597069766657 | 5 | 2638827906663 |
| 39582418599937 | 5 | 15832967439975 |
| 79164837199873 | 5 | 47498902319924 |
| 263882790666241 | 7 | 150790166094995 |
| 1231453023109120 | 3 | 410484341036374 |
| 1337006139375610 | 3 | 445668713125206 |
| 3799912185593850 | 5 | 1519964874237540 |
| 4222124650659840 | 19 | 888868347507335 |
| 7881299347898360 | 6 | 1313549891316390 |
| 31525197391593400 | 3 | 10508399130531100 |
| 180143985094819000 | 6 | 30023997515803300 |
| 1945555039024050000 | 5 | 1167333023414430000 |
| 4179340454199820000 | 3 | 1393113484733270000 |
NTT数论变换的更多相关文章
- 快速傅里叶变换FFT& 数论变换NTT
相关知识 时间域上的函数f(t)经过傅里叶变换(Fourier Transform)变成频率域上的F(w),也就是用一些不同频率正弦曲线的加 权叠加得到时间域上的信号. \[ F(\omega)=\m ...
- 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/常用套路【入门】
原文链接https://www.cnblogs.com/zhouzhendong/p/Fast-Fourier-Transform.html 多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/ ...
- 高速数论变换(NTT)
今天的A题.裸的ntt,但我不会,于是白送了50分. 于是跑来学一下ntt. 题面非常easy.就懒得贴了,那不是我要说的重点. 重点是NTT,也称高速数论变换. 在非常多问题中,我们可能会遇到在模意 ...
- 从傅里叶变换(FFT)到数论变换(NTT)
FFT可以用来计算多项式乘法,但是复数的运算中含有大量的浮点数,精度较低.对于只有整数参与运算的多项式,有时,\(\text{NTT(Number-Theoretic Transform)}\)会是更 ...
- 【算法】快速数论变换(NTT)初探
[简介] 快速傅里叶变换(FFT)运用了单位复根的性质减少了运算,但是每个复数系数的实部和虚部是一个余弦和正弦函数,因此系数都是浮点数,而浮点数的运算速度较慢且可能产生误差等精度问题,因此提出了以数论 ...
- Algorithm: 多项式乘法 Polynomial Multiplication: 快速傅里叶变换 FFT / 快速数论变换 NTT
Intro: 本篇博客将会从朴素乘法讲起,经过分治乘法,到达FFT和NTT 旨在能够让读者(也让自己)充分理解其思想 模板题入口:洛谷 P3803 [模板]多项式乘法(FFT) 朴素乘法 约定:两个多 ...
- 「算法笔记」快速数论变换(NTT)
一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...
- NTT 快速数论变换
NTT 先学习FFT 由于FFT是使用复数运算,精度并不好,而且也无法取模,所以有了NTT(快速数论变换). 建议先完全理解FFT后再学习NTT. 原根 NTT使用与单位根性质相似的原根来代替单位根. ...
- 快速傅里叶变换 & 快速数论变换
快速傅里叶变换 & 快速数论变换 [update 3.29.2017] 前言 2月10日初学,记得那时好像是正月十五放假那一天 当时写了手写版的笔记 过去近50天差不多忘光了,于是复习一下,具 ...
随机推荐
- Java高并发网络编程(五)Netty应用
推送系统 一.系统设计 二.拆包和粘包 粘包.拆包表现形式 现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下: 第一种情 ...
- vue-cli 3.0版本,配置代理Proxy,不同环境不同target(生产环境,uat环境和本地环境的配置)
1.在项目的的根目录下新建vue.config.js 2.新建一个config包,里面存放不同的环境文件,里面包含:pro.env.js(生产环境配置),uat.env.js(测试环境配置),dev. ...
- C/C++ 智能指针线程池
//这个线程池存在一定的BUG 如果没有多线程编程基础的先生请谨慎使用 //我放弃了这种模板方式的线程池,最好不要使用!!!!!!! ThreadPool.h { #ifndef __THREADPO ...
- java——IO(普通文件,二进制文件,压缩文件 )
二进制文件 压缩包
- STL————bitset
C++的 bitset 在 bitset 头文件中,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间. bitset<> bitset1; //无参构造,长度为 ...
- SP7258 SUBLEX - Lexicographical Substring Search(后缀自动机)
传送门 解题思路 首先建\(sam\),然后在拓扑序上\(dp\)一下,把每个点的路径数算出来,然后统计答案时就在自动机上\(dfs\)一下,仿照平衡树那样找第\(k\)小. 代码 #include& ...
- PHP base64_decode+gzinflate压缩和解密代码图文教程
今天碰到的这个问题,是我在更换一个WP主题是遇到.目前的情况如下,我想要把如下的超链接去掉,后台代码找到了在sidebar1.php文件中. 打开此文件发现是一长串的字符: 经过百度.google后, ...
- on() 不支持hover事件
因为 .hover() 是 jQuery 自己定义的事件… 是为了方便用户绑定调用 mouseenter 和 mouseleave 事件而已,它并非一个真正的事件,所以当然不能当做 .bind() 中 ...
- Navicat创建事件,定时更新数据库
一.新建事件 二.在定义里编写要更改的SQL语句 如果SQL语句有多条,需要将SQL语句放在begin...end中 begin update student set num = '0'; updat ...
- Apache Flink 整体介绍
前言 Flink 是一种流式计算框架,为什么我会接触到 Flink 呢?因为我目前在负责的是监控平台的告警部分,负责采集到的监控数据会直接往 kafka 里塞,然后告警这边需要从 kafka topi ...