歐幾里德演算法、擴充套件歐幾里德演算法、乘法逆元
最近看了一本書《程式設計師》裡面說的一個面試題:
求兩個數的最大公約數:
SoEasy的題目看過C 的人都知道怎麼寫這個程式
1.傳統方法:窮舉
#include <math.h>
int main()
{
int m=1970,n=1066,p=0;
p=m<n?m:n;
for(;p>=1;p--)
{
Count++;
if(m%p==0&&n%p==0)
break;
}
printf("最大公約數是:%d /n",p);
printf("迴圈了:%d /n",Count);
return 0;
}
最大公約數是:2
迴圈了:1065次
2.輾轉相除
#include<stdio.h>
int main()
{
int m=1970,n=1066,p=0;
while(m%n != 0)
{
p = m%n;
m = n;
n = p;
Count++;
}
printf("最大公約數是%d/n",n);
printf("迴圈了%d/n",Count);
return 0;
}
最大公約數是:2
迴圈了:10
對於求1970和1066的最大公約數輾轉相除法明顯優秀很多。
我們可以將輾轉相除法做一個變形。p=m%n 然後將p賦給n實際上就是一個交換 可以寫成一個遞迴的形式如下:
int gcd(int m,int n)
{
if(n==0)//遞迴出口
return m;
else
return gcd(n,m%n);
}
這個函式就是傳說中的歐幾里德演算法的描述了。
3.歐幾里德演算法
#include<stdio.h>
int gcd(int m,int n);
int main(int argc, char* argv[])
{
int m=1970,n=1066;
printf("最大公約數是:%d/n",gcd(m,n));
return 0;
}
int gcd(int m,int n)
{
if(n==0)
{
return m;
}
else
{
return gcd(n,m%n);
}
}
最大公約數是:2
遞迴了:10
這個演算法基本原理就是輾轉相除,效率很高。
在面試的人中大部分都是採用第一種傳統的方法。顯然面試官想要的是第二種人了。其實從實現的過程來看後者要顯得簡單一些,遞迴很好理解。可能是我們都習慣於停用一種演算法就解決問題。
4.擴充套件歐幾里德演算法
這個演算法的並不是為了求最大公約數而設計的,但是它同樣可以實現求最大公約數,原理就是Euclid。
擴充套件歐幾里德算主要是解決乘法逆元的問題:A*BModC=1可以將B描述為:A模C的乘法逆元。好了看看演算法吧:
int ExGcd(int a, int b, int &x, int &y);
int main(int argc, char* argv[])
{
int m=550,n=1769;
int x=0,y=0;
int k;
k=ExGcd(m,n,x,y);
printf("550 和 1769的最大公約數是:%d/n550模1769的乘法逆元是:%d/n1769模550的乘法逆元是:%d/n",k,x,y);
return 0;
}
/***************************************
函式:ExGcd
功能:求兩個數的最大公約數和模P的乘法逆元。
輸入:a,b 輸入引數,求這兩個數的最大公約數
和a模b的逆元 或 b模a的逆元。
輸出:x,y 分別表示a模b的逆元和b模a的逆元。
返回:r 表示a b 的最大公約數。
*************************************/
int ExGcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
int r = ExGcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return r;
}
程式中有詳細的解釋。
總結:積累高效演算法很重要!