最大公约数

来源:互联网 发布:mac pro评测视频 编辑:程序博客网 时间:2024/06/08 11:20

原文:http://blog.sina.com.cn/s/blog_8619a25801010xo4.html

 

最大公约数(greatest common divisor),指某几个整数共有的约数中最大的一个。

       1能整除所有整数,因此任意两个整数a和b的最小公约数都是1;而如果两个数的最大公约数也是1的话,则称该两数互素(relatively prime)。
       ----------
       求解办法:
       1.枚举列出各自约数,再找出最大的公约数。
       2.质约数分解法:两数各做质约数分解,然后取出同样有的项乘起来。
       3.Euclid 算法
        如果b|a,则gcd(a,b)=b;即如果b整除a,则存在整数k,使得a=b*k,因此gcd(bk,b)=b;
        如果存在整数t和r,使得a=b*t+r,则gcd(a,b)=gcd(b,r);由于bt是b的所有约数的倍数,a和b的所有公约数都应该能整除r。
            代码实现一:直接用代码来实现辗转相除法,利用递归就能很轻松实现。
  1.             int gcd(int a, int b){
  2.                   return (!b)?a:gcd(b,a%b);
  3.             }

      b==0,return a即为所求,b!=0,当a<b时,gcd(a,b)=gcd(b,a),否则gcd(a,b)=gcd(b,a%b),缩小数值

      或者非递归的实现:

  1.       int gcd(int a, int b){
  2.             int ret;
  3.             while(b!=0){
  4.                    ret=b; b=a%b;a=r;
  5.             }
  6.       }
          代码实现二:对于大整数而言,取模运算(其中用到了除法)的开销是很大的,那么对于大整数的运算采用类似于辗转相除法的分析有gcd(a,b)=gcd(a-b,b),将取模运算转换成简单得多的大整数的减法。
  1.        BigInt gcd(BigInt a, BigInt b){
  2.              if(a<b) return gcd(b,a);
  3.              if(b==0) return a;
  4.              else return gcd(a-b,b);
  5.        }
       缺点,迭代次数比代码一的多,如果遇到gcd(100000000,1)这类情况会很郁闷。
        4.stein算法
        Euclid算法是计算两个数最大公约数的传统算法,无论从理论还是从效率层面上都是好的,但是在大素数是有一个致命的缺陷会显现。
        考虑到现有平台下,一般整数最多也就64位。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位一下的整数模,也不过价格周期而已,但是对于更大的素数,就需要由用户自己来设计,若用户自己采用类似于多位除法的手算过程中的试商法,过程复杂,消耗cpu周期。在上面的代码实现中可以看到用减法实现Euclid算法虽然解决需要使用除法的问题,但是其迭代次数却不理想。对于现代密码算法,要求就是128位以上的素数的情况比比皆是,那有没有更好的解决办法呢?
        Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。和欧几里德算法算法不同的是,Stein算法只有整数的移位和加减法,这对于程序设计者是一个福音。
        首先注意两个结论:
        gcd(a,a)=a,即一个数和他自身的公约数是其自身。
        gcd(a*k,b*k)=k*gcd(a,b),也就是最大公约数运算和倍乘运算可以交换,特殊的,当k=2时,说明两个偶数的最大公约数必然能被2整除。
        在此基础上有stein算法的描述如下:
        (1)如果a=0,b是最大公约数,算法结束
        (2)如果b=0,a是最大公约数,算法结束
        (3)置a1 = a,b1 = b 和c1 = 1
        (4)如果an和bn都是偶数,则a(n+1)=an/2 ,b(n+1)=bn/2,c(n+1)=cn*2
          注意乘2只要把整数左移一位即可,除2只要把整数右移一位即可
        (5)如果an是偶数,bn不是偶数,则a(n+1)=an/2,b(n+1)=bn,c(n+1)=cn
        (6)如果bn是偶数,an不是偶数,则b(n+1)=bn/2,a(n+1)=an,c(n+1)=cn
        (7)如果an和bn都不是偶数,则a(n+1)=|an+bn|/2,b(n+1)=min(an,bn),c(n+1)=cn
        (8)n++,转(1)
          简单代码实现如下:
  1.           int gcd(int a,int b){
  2.                 if(a==0)  return b;
  3.                 if(b==0)  return a;
  4.                 if(a%2==0&&b%2==0) return 2*gcd(a>>1,b>>1);
  5.                 else if(a%2==0) return gcd(a>>1,b);
  6.                 else if(b%2==0) return gcd(a,b>>1);
  7.                 else return gcd(abs(a-b),min(a,b));
  8.           }
     当然判断一个数为奇数偶数也可以不用如代码中的方法用整个数来取模2,可以仅判断最后一位的奇偶性即可。
     算法比较:Euclid算法每次迭代中最恶劣的情况是,a=2b-1,这样,迭代后,r=b-1。如果a小于2^N,这样大约需要4N次迭代。而Stein算法,每次迭代后,显然AN+1BN+1≤ ANBN/2,最大迭代次数也不超过4N次。也就是说,迭代次数几乎是相等的。但是,需要注意的是,对于大素数,试商法将使每次迭代都更复杂,因此对于大素数,Stein算法将更有优势。

       实战练习:最大公约数 (2011年哈尔滨工业大学计算机研究生机试真题)

        reference:
       1.wiki,最大公约数
       2.一切随心的blog, 最大公约数的两种算法
       3.《编程之美》,Page150
       4.《挑战编程:程序设计竞赛训练手册》 Page126

----------Amazing mathematics
0 0
原创粉丝点击