给定两个正整数m和n,求它们的最大公因子,即能够同时整除m和n的最大正整数。
求最大公约数有多种方法,常见的有“质因数分解法”、“短除法”、“辗转相除法”、“更相减损法”。
最直观的解法是枚举法:最大公约数为能整除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)。
若这段代码占程序运行的大部分时间,则关于这段代码的优化:
辗转相除法是一种古老的算法,又被称作为欧几里得算法,源自《几何原本》。辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数。
这个定理可以从数学分析角度上给以证明(来自百科):
设两数为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)。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 } |
若这段代码的运行时间比例占得比较大时,关于代码优化:
更相减损法,又称"等值算法"。出自《九章算术》。这是一种比较好玩有趣的方法,在这种方法之下,用笔计算两个数的最大公约数应该是比较快的,一连串下去就会得到最大公约数。
将两个数列在它处,然后以小数减大数替换大数得到新的两个数,辗转相减求得他们的最大公约数。
如用更相减损法求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 } |
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 } |
给定两个正整数m和n,求它们的最小公倍数,即指该两数共有倍数中最小的一个(0除外)。