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. 使用场景:
- 求最大公约数
– 两个整数的最大公约数等于其中较小的数和两数相除余数的最大公约数。比如:
- 两个数 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。
- 两个数 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 表示余数 ):
-
给定两个正整数 p 、q
-
步骤:
第 一 步 : 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=n0∗q0+r0→r0=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=n1∗r0+r1→r1=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=n2∗r1+r2→r2=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=n3∗r2+r3→r3=r1%r2
⋮ \vdots ⋮
直 到 余 数 r n 为 0 结 束 直到余数 r_{n} 为 0 结束 直到余数rn为0结束 -
解释:
- 如果有 p < q,算法的第一步实际上会把两个数字交换
- 因为 p、q 的有穷性,又每一步的余数都在减小并且不为负数,所以总会存在 n 使得 第 n 步的余数 r 为 0 使算法终止。
- 重点:第 n-1 步的余数就是 p、q 的最大公约数
2. 例子
计算 a = 1071 和 b = 462 的最大公约数的过程如下:
-
从 1071 中不断减去 462 直到小于 462(可以减 2 次,即商 n0 = 2),余数是 147:
1071 = 2 × 462 + 147 → 147 = 1071 % 462 1071 = 2 × 462 + 147→147=1071\%462 1071=2×462+147→147=1071%462 -
然后从462中不断减去147直到小于147(可以减3次,即n1 = 3),余数是21:
462 = 3 × 147 + 21 → 21 = 462 % 147 462 = 3 × 147 + 21→21=462\%147 462=3×147+21→21=462%147 -
再从147中不断减去21直到小于21(可以减7次,即n2 = 7),没有余数:
147 = 7 × 21 + 0 → 0 = 147 % 21 147 = 7 × 21 + 0→0=147\%21 147=7×21+0→0=147%21
此时,余数是 0,算法终止。所以 1071 和 462 的最大公约数是倒数第二步的余数 —— 21,这和用素因数分解得出的结果相同(见上文)。用表格表示如下:
步骤数 | 算式 | 商和余数 |
---|---|---|
0 | 1071 = 462 ∗ n 0 + r 0 1071 = 462*n_{0} + r_{0} 1071=462∗n0+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=21∗n2+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));
}
}