数论基础之模方程初步
扩展欧几里得算法
¶问题描述
¶已知
问题简析
¶令
由于
因此有
方程 (1) 的构造与辗转相除有异曲同工之妙,需要说明的是,
当
时,我们得到一组解当
时,套用辗转相除的框架求出 ,再得到 。
程序实现
¶1234567891011typedef long long LL;void extendEuclid(LL a, LL& x, LL b, LL &y, LL& d) { // d=gcd(a,b) if (!b) { d = a; x = 1; y = 0; } else { extendEuclid(b, y, a%b, x, d); y -= (a / b) * x; }}
上述代码中,将 d = a
的执行顺序放在最前面是一个小技巧,因为这样就可以像下面这样调用函数,此时不需要预先将
123extendEuclid(a, x, b, d, d);// orextendEuclid(a, d, b, d, y);
值得一提的是,扩展欧几里得算法解出的一组解是所有解中
问题扩展
¶求方程
由前文可知,当
时,方程有解。因为方程 的解乘上 就是此方程的一组解。由此,我们可以推出 ,所以,当 时,方程无解。
那有多少个解呢?
如果
是方程的两个解,那么 .
令,显然,
所以,,并且 。 一般地,若
是方程 一组解,那么 也是一组解。其中, , 为任意整数。
逆元
¶对于任意整数
求解逆元
¶如果
,根据欧拉定理,有 ,特别地,当 是素数时, 。另外,由于 ,构造方程 ,用扩展欧几里得算法也可求出 在模 意义下的逆元 .如果
,由前文可知,方程 不存在整数解,故不存在逆元。
程序实现
¶欧拉定理求逆元。当
是素数时, , 的逆元为inv.euler.cpp123456789101112typedef long long LL;LL powerMod(LL A, LL x, LL mod) { // 返回 A^x%modLL ans = 1;for(; x > 0; x >>= 1, A = A * A % mod) {if (x & 1) ans = ans * A % mod;}return ans;}LL inv(LL A, LL mod) {return powerMod(A, mod-2, mod);}扩展欧几里得求逆元
inv.euclid.cpp1234567typedef long long LL;LL inv(LL A, LL mod) {LL x, y, d;extendEuclid(A, x, N, y, d);// 若 A 和 mod 不互质,则不存在逆元return d == 1 ? x : -1;}
中国剩余定理
¶若正整数
问题简析
¶令
构造方程
结合方程组 (2) 和方程组 (4),进而有,
所以,
剩下的问题仅需利用扩展欧几里得算法求出
程序实现
¶12345678910LL crt(int N, int* a, int* m) { LL M = 1, ans = 0; for (int i = 0; i < N; ++i) M *= m[i]; for (int i = 0; i < N; ++i) { LL w = M / m[i], x, y, d; extendEuclid(w, x, m[i], y, d); ans = (ans + x * w * a[i]) % M; } return ans;}
Baby Step Gaint Step
¶求一个整数
问题简析
¶注意到
注意到,令方程两边同乘以
程序实现
¶1234567891011121314151617181920typedef long long LL;unordered_map<LL, int> emp;
int bsgs(LL A, LL B, LL C) { emp.clear(); int M = ceil(sqrt(1.0 * C));
LL AA = 1; for (int x0 = 0; x0 < M; ++x0) { if (!emp.count(AA)) emp[AA] = x0; AA = AA * A % C; }
LL Am = inv(powerMod(A, M, C), C); for (int k = 0; k < M; ++k) { if (emp.count(B)) return emp[B] + k * M; B = B * Am % C; } return -1;}
Extend Baby Step Gaint Step
¶求一个整数
问题简析
¶在分析之前,先看一个定理:若
这个定理不难证明,事实上,仅需构造方程
再回过头来看普通版的 Baby Step Gaint Step 是如何工作的。注意到,
Gaint Step 能够成功走出去的前提是
不难发现,
接下来讨论
若
,结合前面给出的定理,有又因为
和 是互质的,所以存在模 意义下的逆元 ;进一步将同余方程化为由于
和 是互质的,根据前面的分析,直接套用普通版的 Baby Step Gaint Step 即可解决,这部分算出的答案加上 就是原同余方程的解。若
,由于 ,所以无解。
程序实现
¶123456789101112131415161718192021222324252627282930typedef long long LL;unordered_map<LL, int> emp;
int ebsgs(LL A, LL B, LL C) { LL D = 1, cnt = 0; for (LL G; (G = gcd(A, C)) != 1; cnt += 1) { if (B % G) return -1; B /= G; C /= G; D = D * A / G % C; if (D == B) return cnt; }
emp.clear(); int M = ceil(sqrt(1.0 * C));
LL AA = 1; for (int x0 = 0; x0 < M; ++x0) { if (!emp.count(AA)) emp[AA] = x0; AA = AA * A % C; }
B = B * inv(D, C) % C; LL Am = inv(powerMod(A, M, C), C); for (int k = 0; k < M; ++k) { if (emp.count(B)) return emp[B] + cnt + k * M; B = B * Am % C; } return -1;}
练习
¶# | Problem | Category | Solution / Code |
---|---|---|---|
1 | 51Nod/X^A Mod P | BSGS | solution.cpp |
2 | 51Nod/X^A Mod B | 扩展BSGS | solution.cpp |
3 | SPJ/MOD | 扩展 BSGS | solution.cpp |
4 | UVa/Code Feat | 中国剩余定理 | solution.cpp |
小结
¶拖沓了很久,终于耐下性子整理了一下。
鉴于本人能力有限,有误之处欢迎指正。
Related
¶