模法
Sya_Resory · · 算法·理论
因为滚去 whk 鸽了两年的 Montgomery 取模。
众所周知取模很慢,因为一般的除法很慢。但是除一个
由
为了快速计算 Montgomery 的论文里这么写的。先计算
然后考察
由于模数固定,
接着观测一下
需要预处理
复杂度
贴一下 zzt 在 Loj#6466 的实现并且稍微解释一下:
struct u256 {
u128 lo, hi;
u256() {}
u256(u128 lo, u128 hi) : lo(lo), hi(hi) {}
static u256 mul128(u128 a, u128 b) {
u64 a_hi = a >> 64, a_lo = u64(a);
u64 b_hi = b >> 64, b_lo = u64(b);
u128 p01 = u128(a_lo) * b_lo;
u128 p12 = u128(a_hi) * b_lo + u64(p01 >> 64);
u64 t_hi = p12 >> 64, t_lo = p12;
p12 = u128(a_lo) * b_hi + t_lo;
u128 p23 = u128(a_hi) * b_hi + u64(p12 >> 64) + t_hi;
return u256(u64(p01) | (p12 << 64), p23);
}
} ;
struct Mont {
u128 mod, inv, r2;
Mont(u128 n) : mod(n) {
assert(n & 1);
inv = n;
for (int i = 0; i < 6; ++i) inv *= 2 - n * inv;
r2 = -n % n;
for (int i = 0; i < 4; ++i) if ((r2 <<= 1) >= mod) r2 -= mod;
for (int i = 0; i < 5; ++i) r2 = mul(r2, r2);
}
u128 reduce(u256 x) const {
u128 y = x.hi - u256::mul128(x.lo * inv, mod).hi;
return i128(y) < 0 ? y + mod : y;
}
u128 reduce(u128 x) const { return reduce(u256(x, 0)); }
u128 init(u128 n) const { return reduce(u256::mul128(n, r2)); }
u128 mul(u128 a, u128 b) const { return reduce(u256::mul128(a, b)); }
} mont(1);
u256 是一个压位 u128,
Mont 是 Montgomery 乘法封装成一个结构体。mod 是模数 inv 是 r2 是 r2 的初始化我没看懂。assert 用来特判偶数,但是这题数据好像没偶数。
使用时先 mont=Mont(p),每个数都先 x=mont.init(x) 一下。乘法直接 prod=mont.mul(a,b)。还原的时候 mont.reduce 一下自己就行。