算法 001. 辗转相除法(欧几里得算法)求最大公约数

1. 学习内容:

辗转相除法(欧几里得算法)
参考(维基中文):https://zh.wikipedia.org/wiki/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95#%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0

a. 使用场景:
  • 求最大公约数
    – 两个整数的最大公约数等于其中较小的数和两数相除余数的最大公约数。比如:
  1. 两个数 p = 110, q = 121。较小的数为 p = 110,两数相除余数:q / p = 121 / 110 = 1……11,即余数为 11。所以 p = 110,q = 121 的最大公约数就是较小的数 p = 110 和 p、q 相除的余数 11 的最大公约数。110 和 11 最大公约数为 11。
  1. 两个数 a = 99,b = 180。较小的数为 a = 99,两数相除余数:b / a = 180 / 99 = 1……81,即余数为 81。所以 a = 99, b = 180 的最大公约数就是较小的数 a = 99 和 两者余数 81(记为 c = 81)的最大公约数。现在 a = 99 和 b = 180 的最大公约数问题等价于 a = 99,c = 81 的最大公约数问题。
    a = 99,c = 81 的最大公约数,再使用一次上面的计算方法:取两者较小的数 c = 81,两者相除的余数:a / c = 99 / 81 = 1……18(余数记为 d,d = 18),那么 a = 99, b = 180 的最大公约数问题变成了 a = 99, c = 81 的最大公约数问题,又变成了 c = 81,d = 18 之间的最大公约数问题。之后再来一次同样的计算——d = 18,c、d 相除的余数为 81 / 18 = 4 …… 9,问题就变成了 18 和 9 的最大公约数问题。显然最大公约数为 9。
    即最初的问题 a = 99,b = 180 的最大公约数是 9。

2. 学习时间:

2020.10.20


学习产出:

1. 理论

辗转相除法(简称 gcd(p, q),p、q 是两个输入数据)是一种递归算法,每一步计算的输出值就是下一步计算时的输入值。设 k 表示步骤数(从 0 开始计数),算法的计算过程如下(记 k 表示第 k 步,r 表示余数 ):

  1. 给定两个正整数 pq

  2. 步骤:
    第 一 步 : g c d ( p 0 , q 0 ) → p 0 = n 0 ∗ q 0 + r 0 → r 0 = p 0 % q 0 第一步:gcd(p_{0},q_{0})→ p_{0}=n_{0}*q_{0}+r_{0}→r_{0} = p_{0} \% q_{0} gcd(p0,q0)p0=n0q0+r0r0=p0%q0
    第 二 步 : g c d ( q 0 , r 0 ) → q 0 = n 1 ∗ r 0 + r 1 → r 1 = q 0 % r 0 第二步:gcd(q_{0},r_{0})→ q_{0}=n_{1}*r_{0}+r_{1}→r_{1} = q_{0} \% r_{0} gcd(q0,r0)q0=n1r0+r1r1=q0%r0
    第 三 步 : g c d ( r 0 , r 1 ) → r 0 = n 2 ∗ r 1 + r 2 → r 2 = r 0 % r 1 第三步:gcd(r_{0},r_{1})→ r_{0}=n_{2}*r_{1}+r_{2}→r_{2} = r_{0} \% r_{1} gcd(r0,r1)r0=n2r1+r2r2=r0%r1
    第 四 步 : g c d ( r 1 , r 2 ) → p 0 = n 3 ∗ r 2 + r 3 → r 3 = r 1 % r 2 第四步:gcd(r_{1},r_{2})→ p_{0}=n_{3}*r_{2}+r_{3}→r_{3} = r_{1} \% r_{2} gcd(r1,r2)p0=n3r2+r3r3=r1%r2
    ⋮ \vdots
    直 到 余 数 r n 为 0 结 束 直到余数 r_{n} 为 0 结束 rn0

  3. 解释:

  • 如果有 p < q,算法的第一步实际上会把两个数字交换
  • 因为 p、q 的有穷性,又每一步的余数都在减小并且不为负数,所以总会存在 n 使得 第 n 步的余数 r 为 0 使算法终止。
  • 重点:第 n-1 步的余数就是 p、q 的最大公约数
2. 例子

计算 a = 1071 和 b = 462 的最大公约数的过程如下:

  1. 从 1071 中不断减去 462 直到小于 462(可以减 2 次,即商 n0 = 2),余数是 147:
    1071 = 2 × 462 + 147 → 147 = 1071 % 462 1071 = 2 × 462 + 147→147=1071\%462 1071=2×462+147147=1071%462

  2. 然后从462中不断减去147直到小于147(可以减3次,即n1 = 3),余数是21:
    462 = 3 × 147 + 21 → 21 = 462 % 147 462 = 3 × 147 + 21→21=462\%147 462=3×147+2121=462%147

  3. 再从147中不断减去21直到小于21(可以减7次,即n2 = 7),没有余数:
    147 = 7 × 21 + 0 → 0 = 147 % 21 147 = 7 × 21 + 0→0=147\%21 147=7×21+00=147%21

此时,余数是 0,算法终止。所以 1071 和 462 的最大公约数是倒数第二步的余数 —— 21,这和用素因数分解得出的结果相同(见上文)。用表格表示如下:

步骤数算式商和余数
0 1071 = 462 ∗ n 0 + r 0 1071 = 462*n_{0} + r_{0} 1071=462n0+r0 n 0 = 2 , r 0 = 147 n_0=2, r_0=147 n0=2,r0=147
1 462 = 147 n 1 + r 1 462 = 147 n_{1} + r_{1} 462=147n1+r1 n 1 = 3 , r 1 = 21 n_1=3, r_1=21 n1=3,r1=21
2 147 = 21 ∗ n 2 + r 2 147 = 21*n_{2} + r_{2} 147=21n2+r2 n 2 = 7 , r 2 = 0 , 算 法 终 止 n_2=7, r_2=0,算法终止 n2=7,r2=0

3. 计算机实现
3.1 循环实现

3.1.1 伪代码

function gcd(a, b)
    while b ≠ 0
        t ← b
        b ← a mod b
        a ← t
    return a

3.1.2 C++ 实现

#include <iostream>


// 对应上面的算法描述,使用 p、q 作为参数输入
unsigned int gcd(unsigned int p, unsigned int q) {
	
	unsigned int temp = 0;

	while (q != 0) {
		temp = q;
		q = p % q;
		p = temp;
	}

	return p;
}

int main() {
	
	std::cout << gcd(1071, 462) << std::endl;		// 21
	
	return 0;
}

3.1.3 Python 实现

def gcd(p, q):
    
    while(q != 0):
        temp = q
        q = p % q
        p = temp

    return p



if __name__ == "__main__":
    
    print(gcd(1071, 462))

3.1.4 Java 实现

// file_name: temp.java
public class temp{

	static long gcd(long p, long q) {
		
	    long temp = 0;

	    while (q != 0) {
	        temp = q;
	        q = p % q;
	        p = temp;
	    }

	    return p;
	}

	
    public static void main(String[] args) {
        System.out.println(gcd(1071, 462));  			// 21
    }
}


3.2 迭代实现

3.2.1 伪代码

function gcd(a, b)
    if b = 0
       return a
    else
       return gcd(b, a mod b)

3.2.2 C++ 实现

#include <iostream>

unsigned int gcd(unsigned int p, unsigned int q) {
	
	if (q == 0) 
		return p;

	else
		return gcd(q, p%q);
}

int main() {

	std::cout << gcd(1071, 462) << std::endl;	

	return 0;
}

3.2.3 Python 实现


def gcd(p, q):
    
    if q == 0:
    	return p

    else:
        return gcd(q, p%q)


if __name__ == "__main__":
    
    print(gcd(1071, 462))

3.2.4 Java 实现

// file_name: temp.java

public class temp{

	static long gcd(long p, long q) {
		
	    if (q == 0) {
	    	return p;
	    }
	    
	    else {
	    	return gcd(q, p%q);
	    }
	}

	
    public static void main(String[] args) {
        System.out.println(gcd(1071, 462));
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值