struct ModF { u32 m; f64 ivm; ModF(i32 m_) : m(m_), ivm(1.0 / m) {} u32 calc(u64 a) const { u32 r = a - i64(a * ivm) * m; if (r >= m) r -= m; if (r < 0) r += m; return r; }};
Barrett Reduction
考虑除法的一个替代,选取 q,s 使得等式
⌊a/m⌋=⌊2saq⌋,q≈m2s
对尽可能多的 a 成立。由于 a 是整数,设误差是
2sq=m1+ε
当满足 0⩽aε<m1 则除法是精确的,因此选择
q=⌈m2s⌉=⌊m2s+m−1⌋
是比较方便的。可以推得
ε=2sq−m1<2sm2s+m−1−m1=m2sm−1<ε
化简得到 2s>mεm−1。假如 a∈[0,m2),大概需要 s≈3log2m。
struct Barrett { enum { s = 96 }; static constexpr u128 s2 = u128(1) << s; u32 m; u128 ivm; Barrett(u32 m_) : m(m_), ivm((s2 - 1) / m + 1) {} u32 div(u64 a) const { return a * ivm >> s; } u32 calc(u64 a) const { return a - u64(div(a)) * m; }};
乘定值
倘若我们要计算 a×bmodm,其中 b 是定值,可以把上式的一部分合并。
推导过程没什么区别,重新考虑带入式,仍选择
q=⌈m2sb⌉=⌊m2sb+m−1⌋
由于 a 是整数,设误差是
2sq=mb+ε
当满足 0⩽aε<m1 则除法是精确的。推得
ε=2sq−mb<2sm2sb+m−1−mb=m2sm−1<ε
化简得到 2s>mεm−1。假如 a∈[0,m),大概需要 s≈2log2m。
struct MulBarrett { enum { s = 64 }; static constexpr u128 s2 = u128(1) << s; u32 b, m; u64 ivm; MulBarrett(u32 b_, u32 m_) : b(b_), m(m_), ivm((s2 * b - 1) / m + 1) {} u64 div(u64 a) const { return u128(a) * ivm >> s; } u32 calc(u32 a) const { return a * b - u64(div(a)) * m; }};
struct Montgomery { u32 m, ir; static u32 inv_m(u32 m) { u32 x = 1; for (i32 i = 0; i < 5; ++i) x *= 2 - x * m; return x; } Montgomery(u32 m_) : m(m_), ir(-inv_m(m)) {} u32 tran(u32 a) const { return (u64(a) << 32) % m; } u32 val(u32 a) const { // itrans return redc_m(redc(a) - m); } u32 add(u32 a, u32 b) const { return redc_m2(a + b - m * 2); } u32 sub(u32 a, u32 b) const { return redc_m2(a - b); } u32 redc_m(u32 a) const { return a >> 31 ? a + m : a; } u32 redc_m2(u32 a) const { return a >> 31 ? a + m * 2 : a; } u32 redc(u64 a) const { return (a + u64(u32(a) * ir) * m) >> 32; } u32 mul(u32 a, u32 b) const { return redc(u64(a) * b); }};