OJ165结尾
--------------------------------------------------
欧几里得算法:
辗转相除法,用于快速计算两个数字的最大公约数。
还可用于快速求解 a*x + b*y = 1方程的一组整数解
gcd(a, b) a与b两个数最大公约数的值。
gcd(a, b) => gcd(b, a % b)
大问题缩小成小问题,直到某一层的b值为0,得到答案及a的值。
如何证明?
①假设gcd(a, b) = r, 证在b和a%b中也包含r
设:
a = xr
b = yr 其中x, y∈Z,且x,y互素(gcd(x, y) = 1)
a % b = a - kb , k∈Z,k = a // b (//下取整)
= xr - kyr
= r(x - ky)
② 证r 为最大
其中:
b = yr
a % b = r(x - ky),
证明y与x - ky互素即可。
证:gcd(y, x - ky) = 1
令gcd(y, x - ky) = d,其中d恒等于1.
设:
y = md
x - ky = nd , 其中m, n ∈ Z
推导==>
y = md
x = d * (n + km)
又由于gcd(x, y) = 1
所以d只能恒为1
-----------------------------------------------
int gcd(int a, int b) {
if (!b) return a;
return gcd(b, a % b);
}
一行写法:
int gcd(int a, int b) {
return (b ? gcd(b, a % b) : a);
}
最小公倍数:
lcm(a, b) = a*b / gcd(a, b)
设:
a = gcd(a, b) * x
b = gcd(a, b) * y
则:a * b = x * y * gcd^2(a, b)
所以:lcm(a, b) = a*b / gcd(a, b)
int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
最好先除再乘,防止乘爆了int的取值范围。
------------------------------------------------
欧几里得解决二元一次方程组ax + by = 1一组整数值解,a,b,x,y均为整数:
前提a,b互素,即gcd(a, b) = 1
ax + by = 1
推导:
入口: 中间: 尾部边界:
a b b a%b a b
x y x1 y1 x11 y11
中间:b*x1 + (a%b)*y1 = 1
尾部:a*x11 + b*y11 = 1(边界b=0,取x11=1,y11=0)
展开中间:
b*x1 + (a - kb)*y1 = 1
b*x1 + a*y1 - b*ky1 = 1 ,k = a // b (//下取整)
a*y11 + b(x11 - k*y11) = 1
推导至入口:
ax + by =1 ==>
a*y1 + b*(x1 - k*y1) = 1
x是下一层的y
y是下一层的x-ky
扩展欧几里得算法:
ax + by = gcd(a,b)
int ex_gcd(int a, int b, int* x, int* y) {
if (!b) {
*x = 1, *y = 0;
return a;
}
int xx, yy, ret = ex_gcd(b, a%b, &xx, &yy);
*x = yy;
*y = xx - (a%b) * yy;
return ret;
}
思考题:如何去掉xx yy?
int ex_gcd(int a, int b, int* x, int* y) {
if (!b) {
*x = 1, *y = 0;
return a;
}
int ret = ex_gcd(b, a%b, y, x);
*y -= (a / b) * (*x);
return ret;
}
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int count = 0; // 查看递归次数
int ex_gcd(int a, int b, int* x, int* y) {
if (!b) {
*x = 1, *y = 0;
count++;
return a;
}
int xx, yy, ret = ex_gcd(b, a % b, &xx, &yy);
*x = yy;
*y = xx - (a / b) * yy;
count++;
return ret;
}
int ex_gcd1(int a, int b, int* x, int* y) {
if (!b) {
*x = 1, *y = 0;
return a;
}
int ret = ex_gcd1(b, a % b, y, x);
*y -= (a / b) * (*x);
return ret;
}
int main(int argc, char** argv) {
int a, b, x, y;
while (~scanf("%d%d", &a, &b)) {
printf("gcd(%d, %d) = %d\n", a, b, ex_gcd1(a, b, &x, &y));
printf("%d * %d + %d * %d = %d\n", a, x, b, y, ex_gcd(a, b, &x, &y));
printf("count:%d\n", count);
count = 0;
}
return 0;
}