最大公约数算法
1 问题
两个不全为0的非负整数m和n的最大公约数记做 gcd(m,n),代表能够整除(即余数为0)m和n的最大正整数。 求gcd(m,n)的值。
2 欧几里得算法
思路: gcd(m,n) = gcd(n, m mod n ) (m mod n 表示m除以n之后的余数) 。因为gcd(m,0) = m, 所以m最后的取值就是m和n初值的最大公约数
def gcd(m,n):
while n != 0:
r = m % n
m = n
n = r
return m
3 连续整数检测算法
思路: gcd(m,n)的必然不能大于min(m,n), 从t={m,n}开始,检查t能不能整除m和n,如果能则t是最大公约数,如果不能则t=t-1重复这个步骤, 直到t不大于0。
def gcd(m,n):
# corner case
if m == 0: return n
if n == 0: return m
t = min(m, n)
while t > 0 and not (m % t == 0 and n % t == 0):
t -= 1
return t
4 中学时计算最大公约数
思路: 找到m和n的所有质因数,找出两者质因数公共的部分,这些质因数相乘即是最大公约数。
4.1 埃拉托色尼筛选法
衍生问题:如何产生一个不大于给定正整数n的连续质数序列。
思路:一开始初始化一个2-n的连续整数序列作为候选质数,然后在第一个循环中消除2的倍数, 第二个循环中消除3的倍数,第三个循环中消除5的倍数(4已结消除了)。 这样一直下去直到序列中没有可消除的元素为止。
一个优化:考虑到正在消除p的倍数,第一个值得考虑的倍数是p*p, 因为2p,3p,…,(p-1)p已经在先前的步骤中消去。所以如果p*p>n,那么自然没必要继续消除下去
4.2 实现
import math
from functools import reduce
def find_primes(n):
"埃拉托色尼筛选法, 找出不超过n的质数"
l = list(range(2, n+1))
# 心得: 这里遇到在迭代列表的时候,要移除列表的元素,但移除完后列表的后续索引就变了,索引采用了一个额外的数组来表示删除与否
# l = [1,2,3,4] l_index=[True, True, False, True], 就代表着 [1,2,4]
l_index = [True] * (n-1)
p = math.floor(math.sqrt(n))
for i in range(0, p):
if not l_index[i]:
continue
for j in range(i+1, n-1):
if not l_index[j]:
continue
if l[j] % l[i] == 0:
l_index[j] = False
return [ l[i] for i in range(0, n-1) if l_index[i]]
def find_prime_factors(n):
"找出n的质因数"
result = []
all_primes = find_primes(n)
while n >1:
for p in all_primes:
if n % p == 0:
result.append(p)
n = int(n / p)
return result
def get_common_part(l1, l2):
"获取两个数字列表共同的元素"
# 先排序再比较
l1 = sorted(l1)
l2 = sorted(l2)
l1_len = len(l1)
l2_len = len(l2)
i, j = 0, 0
result = []
while i < l1_len and j < l2_len:
if l1[i] == l2[j]:
result.append(l1[i])
i += 1
j += 1
elif l1[i] > l2[j]:
j += 1
else:
i += 1
return result
def gcd(m, n):
if m == 0: return n
if n == 0: return m
common_part = get_common_part(*map(find_prime_factors, [m, n]))
if not common_part:
return 1
return reduce(lambda x, y: x*y, common_part)