洛谷 P1082 同余方程
源代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<cstring>
using namespace std;
long long x, y;//目前方程真正的解 void exgcd(long long a, long long b) { //当前目的:求解 ax + by = gcd(a, b) 这么一个方程 if(b == 0) //a, b不断改变的过程中,b最终必然会成为0 {
x = 1;//当 b=0 时,方程若要成立,则只需令 x=1,y=0 y = 0; return; } exgcd(b, a % b);//把下一层系数传进去(先求下一个方程的解 ) //现在我们已经拿到了下一个方程的解x, y long long tx = x;//暂时存一下x,别丢了 x = y; y = tx - a / b * y; } int main() { long long a, b; cin >> a >> b; exgcd(a, b); x = (x % b + b) % b;//我们求出来的x必然满足方程,但不一定是最小正整数解,所以要进行答案处理 printf("%lld\n", x); return 0; }
这一道题是zhx大佬上课布置下的数论题,求 ax mod b =1 的最小正整数解 x,就要用到扩展欧几里得来求解。
但作为一名蒟蒻,还是有所借鉴:https://www.luogu.com.cn/blog/cicos/solution-p1082 %%%orz
进入正题!
1.首先,若要求 ax mod b = 1 的最小正整数解 x,即为求解不定方程:ax+by=1 ;(注:扩展欧几里得算法可以适用于所有的 ax + by = gcd(a ,b)的不定方程)
原理为:不定方程 ax + by =m 有解的必要条件是:m mod gcd( a ,b )= 0
证明:因为 a 为 gcd( a ,b )的倍数,b 也为 gcd( a ,b )的倍数
又因为 a,b 均为整数,所以 ax + by 也是 gcd( a ,b )的倍数
而 m = ax + by ,那么 m 就是 gcd( a ,b )的倍数, 故 m mod gcd( a ,b )= 0
2. 设 gcd( a ,b )= n ,求 ax + by = n , 可以假设,如果我们事先拥有一组解 p,q,使得 bp + ( a mod b)q = n ;
那么就一定会有:ax + by = bp +( a mod b )q
当这一假设成立时,我们就可以将目标转化为:求出上述式子中能够满足的 x,y ;此时,我们就可以通过求出一组解来推得 x 的最小正整数解
3. 取模运算的实质:a mod b = a - b *( a / b ) ,则上述的等式可进行转化。
即为:bp +( a mod b )q = b*p + ( a - b *( a / b ))*q = b*p + a*q - b *( a / b )* q = a * q + b * ( p -( a / b )* q )
此时,我们可以看出,满足不定方程的一组解就是 :x = q ,y = p -( a / b )* q;
那么,我们就需要进一步地,尽可能地将这一假设满足,就需要再求出 p 和 q
但是,为了要求出 p ,q ,我们又可以写出新的不定方程来尽可能地求出 p 和 q ,而过程就和上述的 1 ,2 ,3 一样,在不断地循环,寻找着能够支持上一组数据的解
这便形成了一种递归
4.递归需要设立终止条件,而在这个过程中,a 会被不断地替换成为 b , 而 b 会被不断地替换为 a mod b (和辗转相除法一样)
最后,我们会得到 an = n ,bn = 0 ;
那么把它回代,即得:an * x + bn * y = n * x + 0 * y = n ;
那么,只要当 x = 1 时,等式即可成立 ,那么只要将其继续回代,就能求出最终的一组答案(并不一定是最小正整数解)
总结一下,扩展欧几里得算法和辗转相除法类似,也都是递归,就是在有限的递归过程中缩小系数,确定最后一组解。
最后,只要将答案 x 化为一个最小正整数即可。