欧几里德算法又称辗转相除法,用于计算两个正整数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); }