1 最大公约数问题
给定两个正整数m和n,求它们的最大公因子,即能够同时整除m和n的最大正整数。
2 最大公约数算法
求最大公约数有多种方法,常见的有“质因数分解法”、“短除法”、“辗转相除法”、“更相减损法”。
(1) 质因数分解法(短除法)
最直观的解法是枚举法:最大公约数为能整除m及n的正整数i,i = [1, min(m, n) ]中最大的那个数。
function max_factor(m, n){ i = 2 max_factor = 1 max_loop = min(m, n) do { if( (0 == m % i) && (0 == n % i) ){ max_factor = i } ++i; }while(i <= max_loop) return max_factor; } |
这段伪码(awk)枚举了所有小于max_loop的整数,找出m与n的最大公约数。时间复杂度为O(n)。
若这段代码占程序运行的大部分时间,则关于这段代码的优化:
- 求余”%”的转化。”m % i"可转化为”t = m –i; while(t >= i) t -= i;”这两种方式的运行时间谁比较小还得看m,i的值,像这样不定的m,还不如求余表达式。如此可以下结论这样的求余语句可以不优化。
- 循环的判断语句。若省略了循环内的判断语句就需要在循环体内加入新判断语句(且每次循环都会执行)的话,这也构不成代码优化的条件。
(2) 辗转相除法
原理
辗转相除法是一种古老的算法,又被称作为欧几里得算法,源自《几何原本》。辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数。
证明1
这个定理可以从数学分析角度上给以证明(来自百科):
设两数为a、b(b<a),用gcd(a,b)表示a,b的最大公约数,r=a mod b 为a除以b以后的余数,k为a除以b的商,即a÷b=k.......r。辗转相除法即是要证明gcd(a,b)=gcd(b,r)。
第一步:令c=gcd(a,b),则设a=mc,b=nc
第二步:根据前提可知r=a-kb=mc-knc=(m-kn)c
第三步:根据第二步结果可知c也是r的因数
第四步:可以断定m-kn与n互素【否则,可设m-kn=xd,n=yd,(d>1),则m=kn+xd=kyd+xd=(ky+x)d,则a=mc=(ky+x)dc,b=nc=ycd,故a与b最大公约数成为cd,而非c,与前面结论矛盾】。
从而可知gcd(b,r)=c,继而gcd(a,b)=gcd(b,r)。证明2
递归伪码
function max_factor(m, n){ if n != 0 return max_factor(n, m %n) return m } |
非递归伪码
function max_factor(m, n){ if n == 0 renturn m while (1) r = m % n if r == 0 return n m = n n = r } |
若这段代码的运行时间比例占得比较大时,关于代码优化:
- 递归比一般的迭代算法要耗时,因为它拥有函数调用的代价。可用非递归算法代替。
- 在非递归代码中,将while中的判断语句移除,因为循环内if r == 0不可少且满足去掉循环内判断语句的条件。
(3) 更相减损法
更相减损法,又称"等值算法"。出自《九章算术》。这是一种比较好玩有趣的方法,在这种方法之下,用笔计算两个数的最大公约数应该是比较快的,一连串下去就会得到最大公约数。
原理
将两个数列在它处,然后以小数减大数替换大数得到新的两个数,辗转相减求得他们的最大公约数。
如用更相减损法求81和36的最大公约数的过程如下:
(81,36) –>(45, 36) --> (9, 36) -->(9, 27)-->(9,18) –> (9, 9)在标红的算式及之间的算式都可以得到最大公约数为9。如此就得到81和36的最大公约数为9,这个方法求最大公约数似乎和程序一样快了,哈哈。伪码
function max_factor(m, n){ if n == 0 && m != 0 return m if m == 0 && n != 0 return n while(m != n) if m > n m -= n else if n > m n -= m return n } |
算法改进
因为在用更相减损法的过程中,一个求余运算就能够判断两个数的最大公约数如(9, 36)的最大公约数为9。在两个数相差较大时如(1, 100 000 000)或在这个算法的调用时间多时,这种O(n)的运算也会变得耗时,故而可将算法改进一下。function max_factor(m,n){ if n == 0 && m != 0 return m if m == 0 && n != 0 return n while(m != n) if 0 == max(m,n) % min(m, n) return min(m, n) if m > n m -= n else if n > m n -= m return n } |
3 最小公倍数问题
给定两个正整数m和n,求它们的最小公倍数,即指该两数共有倍数中最小的一个(0除外)。