快速数论变换 | NTT 初学

快速数论变换 | NTT 初学

前置

  • FFT

  • 原根

    阶:称满足同余方程 ax1modm最小正整数解 xa 的模 m 的阶,记为
    Ordma

    观察到本质就是最短循环节,同时该同余方程类似于欧拉定理:

    aφ(m)1modmam

    那么显然两者的关系是 Ordmaφ(m)。特别地,当 Ordma=φ(m) 时,我们称 am 的一个原根。

    下面给出三个有关原根和阶的事实。

    • 若有 an1modm,则 a 的阶 x 满足 xn

      证明:

      n 带余除法表示成 xq+r(qZ,0r<x) 的形式,其中,x=Ordma。那么显然有 axq(ax)q1q1modm。那么可得

      aranxqanxqaxqan1modm

      因为 x 是满足 ax1modm 的最小正整数解,故有 r=0,也即 n=xq,故 xn

    • 一个数 x 是模 m 的原根当且仅当 x 可以生成所有的可逆元,也即任何与 m 互质 的整数 akaxkmodm

      证明:

      x 不是模 m 的原根,那么其阶必定 <φ(m),不同于 x 的次方就少于 φ(m) 个,但是可逆元是数量为 φ(m),显然不可以生成所有可逆元。

      x 是模 m 的原根,x 的前 φ(m) 个正整数次方分别为 g,g2,g3,,gφ(m)。我们尝试用反证法说明这 φ(m) 个数模 m 的结果互不相同。假设存在 1i<jφ(m) 满足 gigjmodm。那么必定有 gji1,因为 ji<φ(m),显然与 Ordmx=φ(m) 矛盾。

      所以有 xφ(m) 次方对 m 取模的余数恰好是与 m 互质的 φ(m) 个余数的重排后的结果,并且有 gφ(m)=1modm

    • x 是模 m 的一个原根,那么 xk 是原根,当且仅当 gcd(k,φ(m))=1

      证明:

      kφ(m) 互质时,一定可以找到一组 (a,b) 满足 ak+bφ(m)=1。故对于 x 任意次方 xi 都有:

      xixi(ak+bφ(m))(xk)aimodm

      也即,xi 在同于意义下可以表示成 xkai 次方,这说明 xi 可生成所有可逆元,故 xi 为模 m 的逆元。

    原根的存在性:
    原根存在的充要条件是模数能表示成 2,4,pn,2pn,其中 p 是一个奇质数。其数量为 φ(φ(m))=φ(m1)

    原根要怎么求?由前面有关原根的事实可知,要判断一个数 x(1<x<m) 是否是 m 的原根,将 m1 质因数分解成 p1c1p2c2pkck。那只需要检验 x(m1)p1,x(m1)p2,,x(m1)pk 中是否存在一个数 v 使得 v1modx。时间复杂度 O(klogm),其中 km1 的质因子个数。当 m<1018 时,k25,时间开销极小。

NTT

快速傅里叶变换中选定单位根做多项式乘法,其劣势在于无法保证浮点数的精度。考虑用原根替换,会发现它依然满足单位根的三个引理:消去引理、折半引理、求和引理。

void NTT(int *e, int op) {
  for(int i = 0; i < lim; i++) {
		if(i < p[i]) swap(e[i], e[p[i]]); // 蝴蝶变换
	}
	for(int k = 1; k < lim; k <<= 1) { // 迭代
		long long pw = Pow((op == -1 ? Pow(3, Mod - 2) : 3), (Mod - 1) / (k << 1)); // 原根替换单位根
		for(int i = 0; i < lim; i += k << 1) { 
			for(int j = i, o = 1; j < i + k; j++, o = o * pw % Mod) {
				int x = e[j];
				int y = e[j + k];
				e[j] = (x + y * o % Mod) % Mod;
				e[j + k] = (x - y * o % Mod + Mod) % Mod;
			}
		}
	}
	if(op == -1) {
		for(int i = 0; i < lim; i++) {
			e[i] = e[i] * Pow(lim, Mod - 2) % Mod;
		}
	}
}

FFT/NTT 匹配字符串

现有文本串 s, 模式串 t,长度分别为 lslt。规定下标均从 0 开始。为方便处理,先要将字符串都转成整数。

tss 的下标 p 结尾处匹配上,也即对于 i=0lt1,有 splt+i+1=ti

因为完全匹配,所以有

i=0lt1(splt+i+1ti)2=0

i=0lt1(splt+i+12+ti22splt+i+1ti)=0

为了统一下标,将 t 反转。

i=0lt1(sj2+ti22sjti)=0,(i+j=p)

这样就可以用FFT/NTT 求解了。

posted @   ereoth  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示