• 求最大公约数的算法


    欧几里德算法
    欧几里德算法又称辗转相除法,用于计算两个正整数a,b的最大公约数的传统算法。
     
    其计算原理依赖于下面的定理:
    定理:gcd(a,b) = gcd(b,a mod b) (a>b 且a mod b 不为0)
    证明:a可以表示成a = kb + r,则r = a mod b
    假设d是a,b的一个公约数,则有
    d可以整除a,d可以整除b,而r = a - kb,因此d可以整除r
    因此d也是(b,a mod b)的公约数
    因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证。
     
    算法设计:
     1. 令r为a/b所得余数(0≤r<b)
     若 r= 0,算法结束;b 即为答案。
     2. 互换:置 a←b,b←r,并返回第一步。
    int gcd(int a,int b)
    {
        if(b==0)
            return a;
        return 
            gcd(b,a%b);
    }
    尼考曼彻斯法
    尼考曼彻斯法的特色是做一系列减法,辗转相减,从而求得最大公约数。例如 :两个自然数35和14,用大数减去小数,(35,14)->(21,14)->(7,14),此时,7小于14,要做一次交换,把14作为被减数,即(14,7)->(7,7),再做一次相减,结果为0,这样也就求出了最大公约数7。
    int gcd(int a, int b)
    {
      if(a==0)
        return b;
      if(b==0)
        return a;
    while (a != b) { if (a > b) { a = a - b; } else { b = b - a; } } return a; }
     

    Stein算法

    Stein算法是一种计算两个数最大公约数的算法,它是针对欧几里德算法在对大整数进行运算时,需要试商导致增加运算时间的缺陷而提出的改进算法。

    欧几里德算法缺陷

    欧几里德算法是计算两个数最大公约数的传统算法,无论从理论还是从实际效率上都是很好的。但是却有一个致命的缺陷,这个缺陷在素数比较小的时候一般是感觉不到的,只有在大素数时才会显现出来。
    一般实际应用中的整数很少会超过64位(当然现在已经允许128位了),对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算128位以上的素数的情况比比皆是,设计这样的程序迫切希望能够抛弃除法和取模。
    算法思想
    由J. Stein 1961年提出的Stein算法很好的解决了欧几里德算法中的这个缺陷,Stein算法只有整数的移位和加减法,为了说明Stein算法的正确性,首先必须注意到以下结论:
    gcd(a,a)=a,也就是一个数和其自身的公约数仍是其自身。
    gcd(ka,kb)=k gcd(a,b),也就是最大公约数运算和倍乘运算可以交换。特殊地,当k=2时,说明两个偶数的最大公约数必然能被2整除。
    当k与b互为质数,gcd(ka,b)=gcd(a,b),也就是约掉两个数中只有其中一个含有的因子不影响最大公约数。特殊地,当k=2时,说明计算一个偶数和一个奇数的最大公约数时,可以先将偶数除以2。
    int gcd(int a, int b)
    {
        // 交换a,b的值,保证a>=b
        if (a<b)          
        {
            int temp = a;
            a = b;
            b = temp;
        }
    
        if (b==0)
            return a;
    
        // a,b均为偶数(避免使用除法和取模运算)
        if ((a&0x1)==0 && (b&0x1)==0) 
            return 2*gcd(a>>1, b>>1);
        
        // a为偶数,b为奇数
        if ((a&0x1)==0 && (b&0x1)!=0) 
            return gcd(a>>1, b);
        
        // a为奇数,b为偶数
        if ((a&0x1)!=0 && (b&0x1)==0) 
            return gcd(a, b>>1);
        
        // a,b均为奇数 这里应用了尼考曼彻斯法,两个奇数相减,结果为偶数,又可以用/2的方法
        if ((a&0x1)!=0 && (b&0x1)!=0) 
            return gcd((a-b)>>1, b);
    }
     
     
  • 相关阅读:
    冲不动刺。。
    第六次作业——团队作业
    LeetCode 638 Shopping Offers
    windows 64bit 服务器下安装32位oracle database 11g 问题集
    Codeforces Round #379 (Div. 2) D. Anton and Chess 模拟
    Codeforces Round #381 (Div. 2) D. Alyona and a tree 树上二分+前缀和思想
    HDU 1171 Big Event in HDU 多重背包二进制优化
    HDU 3401 Trade dp+单调队列优化
    HDU 5976 Detachment 打表找规律
    HDU 5973 Game of Taking Stones 威佐夫博弈+大数
  • 原文地址:https://www.cnblogs.com/scarecrow-blog/p/3728414.html
Copyright © 2020-2023  润新知