NTT N T T 相关
一种快速数论变换算法,这种算法是以数论为基础,对样本点为的数论变换,按时间抽取的方法,得到一组等价的迭代方程,有效高速简化了方程中的计算公式·与直接计算相比,大大减少了运算次数。(见快速傅里叶变换)。
在计算机实现多项式乘法中,我们所熟知的快速傅里叶变换(FFT)是基于n次单位根 (omega) 的优秀性质实现的,而由于其计算时会使用正弦函数和余弦函数,在不断运算时无法避免地会产生精度误差。而多项式乘法有些时候会建立在模域中,在对一些特殊的大质数取模时,便可以考虑用原根g来代替 ,而这些特殊的大质数的原根恰好满足 的某些性质,这使得多项式乘法在模域中也可以快速的分治合并。
——百度百科
NTT(Number Theoretic Transform),中文名快速数论变换
和 FFT F F T 一样, NTT N T T 也用来加速多项式乘法,不过 NTT N T T 最大的优点是可以取模
或者可以理解为 NTT N T T 是 FFT F F T 取模升级版
- 看这篇 NTT N T T 之前确保你已经会了 FFT F F T →奶一口本人 FFT F F T 博客
- 好像 NTT N T T 比起 FFT F F T 来难的知识点更少了emm
NTT N T T 的优缺点
优点
能取模, FFT F F T 复数你给我取个模……?
没有精度差, FFT F F T 浮点数精度怎么也会出点问题
由于均为整数操作(虽然取模多), NTT N T T 常数小,通常比一大堆浮点运算的 FFT F F T 要快(其实这是放屁)
NTT N T T 小数据下表现良好……
缺点
多项式的系数都必须是整数
模数有限制而且 NTT N T T 题目的模数通常都是相同的 998244353 998244353
NTT N T T 前置知识&技能
原根
对于 g,p∈Z g , p ∈ Z ,如果 g i modp(1⩽i⩽p−1) g i m o d p ( 1 ⩽ i ⩽ p − 1 ) 的值互不相同,则称 g g 为 p p 的原根
或者说 ∀i,j(1⩽i,j⩽p−1,i<j),g i modp≠g j modp ∀ i , j ( 1 ⩽ i , j ⩽ p − 1 , i < j ) , g i m o d p ≠ g j m o d p ,那么 g g 为 p p 的原根
原根没什么快速求法,只能暴力枚举判断
通常模数常见的有 998244353,1004535809,469762049 998244353 , 1004535809 , 469762049 ,这几个的原根都是 3 3
就这么少东西?好像真的只有这么少
NTT N T T (快速数论变换)
FFT F F T 可以优化是因为 ω ω 有着神奇且优秀的性质
NTT N T T 呢?其实原根也有这类性质!
在 NTT N T T 里,我们可以拿原根来代替 FFT F F T 的单位根
具体就是,当合并区间的长度为 len=2mid l e n = 2 m i d 时,单位根为 cos2πlen +isin2πlen =cosπmid +isinπmid cos 2 π l e n + i sin 2 π l e n = cos π m i d + i sin π m i d
而原根即为 g p−1len =g p−12mid g p − 1 l e n = g p − 1 2 m i d
注意大多题目的模数 p=998244353 p = 998244353 ,此时 g=3 g = 3 ,即可代入原根计算 NTT N T T 了
如果理解了 FFT F F T 的话, NTT N T T 也就迎刃而解了
时间复杂度 O(nlog 2 n) O ( n log 2 n )
NTT N T T 板子
#define g 3//模数的原根
#define mod 998244353//通常情况下的模数
int pow(int x,int y)//快速幂
{
ll z=1ll*x,ans=1ll;
for (;y;y/=2,z=z*z%mod)if (y&1)ans=ans*z%mod;//注意精度
return (int)ans%mod;
}
以下是 NTT N T T 板子,基本就是拿 FFT F F T 的板子改几下就好了
void ntt(int a[],int inv)
{
int bit=0;
while ((1<<bit)<n)bit++;
fo(i,0,n-1)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
if (i<rev[i])swap(a[i],a[rev[i]]);
}//前面和FFT一样
for (int mid=1;mid<n;mid*=2)
{
int temp=pow(g,(mod-1)/(mid*2));//用原根代替单位根
if (inv==-1)temp=pow(temp,mod-2);//逆变换则乘上逆元
for (int i=0;i<n;i+=mid*2)
{
int omega=1;
for (int j=0;j<mid;j++,omega=1ll*omega*temp%mod)
{
int x=a[i+j],y=1ll*omega*a[i+j+mid]%mod;
a[i+j]=x+y,a[i+j+mid]=x-y;
a[i+j]=(a[i+j]%mod+mod)%mod,a[i+j+mid]=(a[i+j+mid]%mod+mod)%mod;//注意取模
}
}//大体流程和FFT无异
}
}
和 FFT F F T 一样,也只能处理 n n 为 2 2 的次幂的多项式
NTT N T T 调用和 FFT F F T 一模一样,注意 longlong l o n g l o n g 和除法都变成乘逆元
逆 NTT N T T 变换后每一项系数乘上模数 p p 的逆元即可
NTT N T T 这就没了……?
本人版权意识薄弱……
其实 NTT N T T 还有更高级、更黑科技的用法
任意模数NTT以后慢慢学好了今天一天搞完NTT肾都虚了