编程之美——2.7 最大公约数

/**
 * 本程序用于求解两个正整数的最大公约数
 * 求解最大公约数往往可以用的有三种方法:
 * 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值