编程之美——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;

}

    原文作者:mxk19930509
    原文地址: https://blog.csdn.net/u011387543/article/details/80593127
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞