转:https://www.cnblogs.com/zhanhonhao/p/11329772.html
欧几里得算法:
欧几里得算法,也叫辗转相除,简称 gcd,用于计算两个整数的最大公约数
原理:辗转相除法求解最大公约数(公因子)
设两数为a、b(a>b),用gcd(a,b)表示a,b的最大公约数
r=a (mod b) ()为a除以b的余数,q为a除以b的商,即a÷b=q.......r。辗转相除法即是要证明gcd(a,b)=gcd(b,r)。
证明: a/b = q; a = q*b+r;
<1>当 r=0时,b为a与b的最大公约数
<2>当 r!=0时, 设a、b的最大公约数为c, a = m*c; b=n*c; (因为c为a、b的最大公约数,所以m,n互质)
r = a-q*b; r = (m*c)-q*b(n*c)=(m-q*b*n)*c,由此可知c也是r的约数,
所以我们可以继续b/r直到r为0,我们就得出a,b的最大公约数了
递归:
#include<iostream> using namespace std; int gcd(int a,int b){ if(b==0) return a; return gcd(b,a%b); } int main() { int a,b,ans; cin>>a>>b; ans = gcd(a,b); cout<<ans; return 0; }
优化:
#include<iostream> using namespace std; int gcd(int a,int b){ return b?gcd(b,a%b):a; } int main() { int a,b,ans; cin>>a>>b; ans = gcd(a,b); cout<<ans; return 0; }
最小公倍数lcm: a=m*c b=n*c;m与n互质,所以lcm(a,b)=a*b/c
#include<iostream> using namespace std; int gcd(int a,int b){ return a%b?gcd(b,a%b):b; } int main() { int a,b,c,ans; cin>>a>>b; c = gcd(a,b); ans = a*b/c; cout<<ans<<endl; return 0; }
扩展欧几里得算法:
扩展欧几里得算法,简称 exgcd,一般用来求解不定方程,求解线性同余方程,求解模的逆元等
扩展欧几里得算法:求a*x+b*y=c的通解。
裴蜀定理:对于整数a,b,他们关于x,y的线性不定方程ax+by=d,设gcd(a,b)=g,
则可证明g|d,换句话说,就是g是a,b的最小线性组合
证明:
. 设a*x+b*y=t,
<1>当b=0时,t=a(因为gcd算法,if(b==0) return a;),则有a*x=a,易得x=1.
<2>当b!=0时,设a*x1+b*y1=gcd(a,b),b*x2+(a%b)*y2=gcd(b,a%b);由于gcd(a,b)=gcd(b,a%b),联立有:a*x1+b*y1=b*x2+(a%b)*y2。
a%b=a-(a/b)*b;(a/b向下取整)。
则 ax1+by1=bx2+(a-a/b*b)y2
ax1+by1=bx2+ay2-a/b*by2
ax1+by1=ay2+bx2-b*a/b*y2
ax1+by1=ay2+b(x2-a/b*y2)
解得 x1=y2 , y1=x2-a/b*y2
根据上面的证明,在实现的时候采用递归做法,先递归进入下一层,等到到达最后一层即 b=0 时就返回x=1 , y=0
再根据 x=y’ , y=x’-a/b/y’ ( x’ 与 y’ 为下一层的 x 与 y ) 得到当层的解。不断算出当层的解并返回,最终返回至第一层,得到原解
int exgcd(int a,int b,int &x,int &y){ if(b==0){ x=1; y=0; return a; } int r=exgcd(b,a%b,x,y); int t=x; x=y; y=t-a/b*y; return r; }
乘法逆元:
在同余问题中,有ax≡1(mod p),其中p∈{素数},则称x为a在mod p域下的逆。
拓展欧几里得求逆元
PS: a ≡ b (mod m) 读作a同余于b模m,或读作a与b关于模m同余。 比如 26 ≡ 14 (mod 12)。
什么是逆元?a∗x≡1 (mod m) ,这里x就是a的逆元。逆元有什么用呢?如果我们要求a/b mod m的值,而a,b很大,设b的逆元为x,
这个时候注意到(a/b)∗x∗b=(a/b) mod m= a ∗ x mod m巧妙地把出发转换成了乘法。
为什么求逆元跟欧几里得算法联系起来了呢?根据上面裴蜀定理,我们知道gcd是a,b两个数线性组合的最小值,其他组合值都是gcd的倍数,
当gcd为1时,a,b互质,满足ax+by=1,移项得ax=−by+1,即ax≡1 mod b ,此时的x就是逆元。实际上线性不定方程组有无穷多解,这里只求正的最小的逆元。
int cal(int a,int m) { int x,y; int gcd = ex_gcd(a,m,x,y); //cout << "a " << a << " m " << m << " x " << x << " y " << y << endl; if(1%gcd!=0) return -1; x*=1/gcd; m = abs(m); int ans = x%m; if(ans<=0) ans += m; return ans; }
这里1%gcd是看gcd是不是1,前面说了,d(这里是1)应该是gcd的倍数,而且不互质的两个数没有逆元。
x∗=1/gcdx∗=1/gcd实际上更一般的写为x∗=d/gcdx∗=d/gcd,也就是求解一般不定方程ax+by=dax+by=d的解,因为d是gcd的倍数,我们就把倍数乘上去解得x′=x∗(d/gcd)x′=x∗(d/gcd)。
m是负数的话,我们取|m|,如果求出来x是负数,就x%|m|,结果再加上|m|即可。(因为有无穷个解,通解为x+m∗tx+m∗t)
PS:
模运算:( a + b ) mod p = ( a mod p + b mod p ) mod p
( a * b ) mod p = ( (a mod p) * (b mod p) ) mod p