1.欧几里得算法
计算a, b的gcd(最大公约数),设k = gcd(a, b),则a = pk, b = qk。这里假设a > b,那么a % b = c,c = a - tb,将a,b替换掉得到c = pk - tqk,这里可以看见k也是c的约数。那么通过这样不停得取余,最终会得到x % 0 ,这时最大公约数就是x
示例30和35
35 % 30 = 5
30 % 5 = 0
所以最大公约数是5
int gcd(int a, int b){
return b == 0 ? a : gcd(b, a % b);//如果这里a < b的话经过一次递归会将a, b互换
}
复杂度是O(logN)
2.扩展欧几里得
如果gcd(a, b) = k,则一定存在x, y使得ax+by=k成立。特别的当a, b互质的时候ax+by=1(详见斐蜀定理)
这里扩展欧几里得是也要求出来x, y
ax' + by' = gcd(a, b)
bx + (a % b)y = gcd(a, b),这里因为gcd(a%b, b) = gcd(a, b)
那么a % b = a - a / b * b
带入得到ay + b(x - a / b * y) = gcd(a, b)
那么们已知x, y就可以求的x‘,y’了
x‘ = y
y’ = x - a / b * y
那么我们怎么能知道一对x和y呢,回想一下我们的终止条件,是不是余数等于0这时的b就是我们的gcd
实例66, 42
66 % 42 = 24
42 % 24 = 18
24 % 18 = 6这里的式子的答案是不是gcd(66,42),我们从这里可以轻易知道24
18 % 6 = 0
6 % 0这时式子终止
那么我们以a = 6, b = 0,得到6x - 0y = 6,所以这里我们赋值x = 1,y = 0
这样我们就可以不停向上计算了。
int extgcd(int a, int b, int &x, int &y){
int d = a;
if(b != 0){
d = extgcd(b, a % b, y, x);
y -= a / b * x;
}
else{
x = 1, y = 0;
}
return d;
}
扩展欧几里得应用,主要是为了求逆元来求解同余方程,我们有同余方程ax ≡b(mod p),那么我们称A*A‘≡1(mod p),我们称A’为A在mod p下面的逆元,也可以写A^-1,不这样写主要是为了和1 / A分开。我们化简一下这个式子A*A‘ - t * p = 1,在这里是不是和我们的斐蜀定理的式子很像。当gcd(A, p) = 1的时候逆元是存在的,而当gcd(A, p) != 1的时候逆元是不存在的。
那么原式就等于a’ * a * x ≡ b * a‘(mod p),应为a’*a在mod p的时候为1,所以又等于x≡b * a‘(mod p)原式求解。
同余里面还有关于除法的定义,如果AC≡BC(MOD P),就可以化简为A≡B(MOD P / gcd(C, P)),这样又可以和费马小定理对应上。
所以在求解已知A = BT,A % P = C,求A / B % P的答案就相当于BT≡C(mod p), 得到B'BT≡CB'(MOD P),得到T≡CB'(mod p),所以A ≡ B(mod P),那么A / C % P 就等于B * C’(MOD P),前提是C必须在mod P下有逆元。
(这里写的有点乱,请见谅。。。。)
逆元的模板
int mod_inversion(int a, int p){
int x, y;
int d = extgcd(a, p, x, y);
if(d == 1){
return (x % m + m) % m;//x可能求出有负数的情况,这里转为正数
}
return -1;//没有逆元
}