1. 程式人生 > >歐幾里德演算法、擴充套件歐幾里德演算法、乘法逆元

歐幾里德演算法、擴充套件歐幾里德演算法、乘法逆元

最近看了一本書《程式設計師》裡面說的一個面試題:

求兩個數的最大公約數:

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;
}

程式中有詳細的解釋。

總結:積累高效演算法很重要!