/**
* 本程序用于求解两个正整数的最大公约数
* 求解最大公约数往往可以用的有三种方法:
* eg: 求正整数x和y的公约数
* 1. 遍历, 从1遍历到min(x, y)为止, 找到能够同时被两数整除的最大整数
* 2. 辗转相除法, 取k = x/y, b = x % y, 则 x = k * y + b; 如果一个数能同时整除x和y, 则其一定能同时整除b和y, 即x和y的公约数与b和y的公约数是相同的, 其最大公约数也相同. 所以f(x, y) = f(y, x % y) (x >= y > 0)
* 辗转相除法证明:
* If x和y, 其最大公约数为d, 则 x = k1 * d, y = k2 * d; 令k = x / y, b = x % y, 则x = k * y + b. 代入, 则k1 * d = k * k2 * d + b, 等号左右相等, 且等式中所有的字符均代表一个整数, 因此等式左右两侧均能被d整除.
* 等号右侧中, k * k2 * d本身可以被d整除, 因此b也一定能被d整除, 因此d也一定是b和y的公因数.
* 现在证明d是b和y的最大公因数.
* 假设b和y存在更大的公因数D > d,
* 则在等式x = k * y + b中, b / D和k * y / D的结果均为整数, 因此x / D也为整数, 即x / D = k * y / D + b / D. 则此时x和y均能被D整除, 因此D为x和y的公因数, 又因d为x和y的最大公因数, 而假设D > d, 互相矛盾了, 所以假设不成立
* 即, b和y不存在更大的公因数, 即b和y的最大公因数也是d.
*/
#include<stdio.h>
int Division(int x, int y) {
if (!y)
return x;
else
return Division(y, x % y);
}
/**
* 3. 相减法. 如果一个数能同时整除x和y, 则其一定能同时整除x – y和y(x > y), 即f(x, y) = f(x – y, y).
* 与辗转相除法相比, 相减法可以用减法运算代替求模运算, 相对开销要小一些, 但是程序迭代的次数却多了很多, 尤其是在f(10000, 1)这种类型的运算时.
* 相减法证明:
* 与辗转相除法相同, f(x, y) = d, x = k1 * d, y = k2 * d, x = k * y + b.
* 则: x – y = k * y – y + b, 两边同时除以d, 得到, k1 – k2 = (k – 1) * k2 + b / d, 上文中已经证明b可以被d整除因此等式成立, 且左右均为整数, 因此x – y可以被d整除.
* 与上文同样方法可以证明d为x – y和y的最大公约数.
*/
int Minus(int x, int y) {
if (x < y) {
x = x ^ y;
y = x ^ y;
x = x ^ y;
}
if (y == 0)
return x;
return Minus(x – y, y);
}
/**
* 4. 改进算法, 结合辗转相除法和相减法
* 此算法的主要依据为两个公式:f(x, y) = f(k * x1, k * y1) = k * f(x1, y1), f(x, y) = f(p * x1, y) = f(x1, y)(p是素数且y不能被p整除)
* 对于x和y, 如果x = k * x1, y = k * y1, 则f(x, y) = f(k * x1, k * y1) = k * f(x1, y1).
* 证明:
* 假设d = f(x, y), 即d为x和y的最大公约数. 因此x = d * x2, y = d * y2.
* 假设k不能整除d, 则如下所示:
* d * x2 = k * x1, d * y2 = k * y1. 等号两边同时除以k, 由于d不能被k整除, 则x2和y2必然能被k整除, 因此x2和y2必然存在公因子k, 因此d不为x和y的最大公因数, 与假设不相符所以k能整除d.
* 等式两边同时提取k, 得到d1 * x2 = x1, d1 * y2 = y1, d1 = f(x1, y1), 且d = d1 * k. 等式成立.
* 对于第二个公式f(x, y) = f(p * x1, y) = f(x1, y)(p是素数且y不能被p整除)
* 证明:
* 令 x = p * x1(p是素数且y不能被p整除), d = f(x, y)
* 因为y不能被p整除, 所以p不能整除d(如果p能整除d, 则y必然能整除p), 又p为素数, 则d不能整除p, 因此d和p是x的两个独立的因数, 即p和d的最大公因数为1,
* 所以x = p * x1 = p * d * x2, 即x1能被d整除.
* x1 < x且为x的因子, 所以d同时为x1和y的公因子(同样可用反证法来证明一下)
*/
/**
* 将改进算法用于求解最大公因子, 由于需要做除法运算, 而在计算机的表达中, 对2做除法运算可通过右移一位来轻松实现, 因此p可以取2.
* 因此对于x和y, 求最大公约数f(x, y), 便可优化为:
* x, y均为偶数, return 2 * f(x >> 1, y >> 1)
* x, y均为奇数, f(y, x – y)
* x为奇数, y为偶数, f(x, y >> 1)
* x为偶数, y为奇数, f(x >> 1, y)
*/
/**
* 此外对于判断x和y是否为偶数的方法, 如果除法运算的话, 改进就没有意义了, 因此可以通过与运算来实现
* 对于二进制表示中,偶数的最低位为0, 奇数为1, 可通过这一特性来判断是否为偶数, 一个&运算即可.
*/
// 判断x是否为偶数. 偶数返回1, 奇数返回0
int IsEven(int x) {
return (x & 1) ? 0 : 1;
}
int Improve(int x, int y) {
if (x < y)
return Improve(y, x);
if (y == 0)
return x;
if (IsEven(x)) {
if (IsEven(y))
return 2 * Improve(x >> 1, y >> 1);
else
return Improve(x >> 1, y);
} else {
if (IsEven(y))
return Improve(x, y >> 1);
else
return Improve(x – y, y);
}
}
int main() {
int x, y;
scanf(“%d%d”, &x, &y);
printf(“%d和%d的最大公因数为%d\n”, x, y, Division(x, y));
printf(“%d和%d的最大公因数为%d\n”, x, y, Minus(x, y));
printf(“%d和%d的最大公因数为%d\n”, x, y, Improve(x, y));
return 0;
}