[Tricks-00008]浅谈取模技巧
取模从古以来就是一个难题。
比如有一个数 p,和两个数 0≤x,y<p,计算 (xymod。暴力做除法的复杂度是 O(\dfrac{\log x\log p}{w}),这里请注意。高精度乘除的复杂度是不同的,这是因为乘法可以把两边都压位,而除法必须枚举一个。接下来,我们介绍 Montgomery 形式,不仅可以优化取模,还能干其它事。
有函数 R(x),定义域是 [0,p),返回 xr\bmod p。R'(x),定义域是 [0,p^2),返回 \dfrac{x}{r}\bmod p,注意它们的值域都是 [0,p),可是定义域不同,因此它们并不互为反函数。
如何求 R'(x)?
答:设 \dfrac{x}{r}\equiv t\bmod p,则 x\equiv tr\bmod p。改写为 x=tr+kp。你现在要求出一个 [0,p) 的 t。
换模数(这也是数论问题常见的处理技巧?),改为两边同时 \bmod r,则 kp\equiv x\bmod r,解得 k=(xp^{-1}\bmod r)。
这样我们得到了一个 [0,r) 的 k。又知 t=\dfrac{x-kp}{r},x\in [0,p^2),所以有 |t|<\dfrac{p^2+rp}{r}。
请注意,刚才我们并没有说 r 是几。但是从中可以看出特点:要好求逆(也说明要和 p 互素,这里 p 必须是质数),且 r>p。最好的办法是让 r 取成一个 2^{2^q} 形式。则 -p<t<2p,稍作修改即可将 t reduce 为 [0,p) 的数。取模关键部分在这里。
下一个问题:如何求 R(x)?
因为 x\equiv \dfrac{xr^2}{r}\bmod p,所以改为算 R'(xz) 即可,其中 z=(r^2\bmod p),可以预处理!
请注意,下面要说一个关键点。我们从此考虑那些运算时,不要 再把那些原来的 x 带入计算了,通通改成 R(x)。这为什么是可行的?
第一点:R(x\pm y)=R(x)\pm R(y),reduce 是极其容易的。
第二点:R(xy)\equiv\dfrac{R(x)R(y)}{r}\bmod p,因此有 R(xy)=R'(R(x)R(y))。这里注意 R' 的定义域可以有平方,因此直接一个乘法一个 reduce 就好了。
第三点:也就是最后怎么还原?其实也非常容易:x=R'(R(x))。
于是就赢麻了。假如我们高精度都是在 2 进制意义下计算,则对一个 2 的次幂取模是轻松的(大概相当于 resize)。最后一个问题,计算一个奇数在 \bmod 2^{2^q} 意义下的逆元。(注 update:实际上,对于低精度运算,比如 ull,取模 2^64 是一个很好的选择;可是如果高精度,更好的办法是直接随便取一个 2^u,并不一定要求满足是 2 的幂,同样可以牛顿迭代,直接递归就好啦)
这东西像不像多项式求逆?没错,牛顿迭代即可。
设要求出 a 的逆元,已经有的数是 b,每轮 b\leftarrow 2b-b^2a 即可。因为倍增,所以这部分复杂度 O(\dfrac{\log^2 p}{w^2}),很可能不足以成为复杂度瓶颈。其实,这里还是有一个地方要取模的,因为在计算 R(x) 的时候首先要求出 r^2\bmod p,一般没有很好的办法。
每次取模的复杂度也降为了 O(\dfrac{\log^2 p}{w^2})。快速幂的时候最喜欢干的事情它就来了。
最后总结:我们有了一个在 O(\dfrac{\log^2 p}{w})-O(\dfrac{\log^2 p}{w^2}) 时间复杂度内解决了对一个固定的数多次取模问题。实测相比于普通的取模,优化效果十分显著。
当然,不止针对于高精度,平常取模题 montgomery 也是神器。快快应用到你的任意模数 NTT 当中去吧!
最后,笔者没测过 Barrett 和 Montgomery 两种算法效率的对比。有兴趣的可以自己试下,欢迎与我讨论(其实我对这部分也是一窍不通 qwq)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)