演算法初體驗之歐幾里得演算法
通過本文希望能給程式設計的初學者一些啟發。
本文重點講述歐幾里得演算法,引出演算法的三大前提,大概闡明演算法的一些特點。
歐幾里得演算法(或輾轉相除法)用於計算兩個正整數的最大公約數,基本演算法如下:
E:設兩個正整數m,n,且已知m>n
E1:令r=m%n('%'代表取餘)
E2:若r=0(即n整除m),結束運算,n即為結果
E3:否則令m=n,n=r,並返回步驟E1
歐幾里得演算法運用了這樣一個等價式(設gcd(m,n)代表m和n的最大公約數,mod()代表取餘運算或模運算)gcd(m,n)=gcd(n,mod(m,n))。也就是m,n的最大公約數等於他們相除餘數(r)和n的最大公約數。
下面來證明一下這個等式:
1.因為任意兩個正整數都有最大公因數,設為d
2.將m,n分別用最大公因數d來表示為m=k1d,n=k2d(k1,k2是兩個常數)
3.設k3=m/n('/'代表相除取整),有r=m-k3n,將m,n代換得r=k1d-k3k2d,所以r=(k1-k3k2)d
由此可得r是最大公因數d的倍數,得證gcd(m,n)=gcd(n,mod(m,n)),所以以此類推,可以將m,n中較大的數用較小的餘數r替換,實現了降維,所以有了E3中的步驟,而歐幾里得演算法的優點就在於此。
相比之下利用窮舉法來計算最大公約數,其比較次數與計算量都大出很多。
C語言實現歐幾里得演算法程式碼如下:
//功能:計算兩個正整數的最大公約數
//引數:m,n
//返回:m,n的最大公約數,型別為int整形
#include <stdio.h>
#include <stdlib.h>
int gcd(int m,int n)
{
int t,r;
if (m<n)//為了確保是大數除小數
{
t=m;
m=n;
n=t;
}
while((m%n)!=0)//輾轉相除
{
r=m%n;
m=n;
n=r;
}
return n;
}
int main()//主函式呼叫函式gcd()
{
int m,n;
scanf("%d%d",&m,&n);
printf("%d",gcd(m,n));
return 0;
}
採用窮舉實現最大公約數的演算法的C語言實現如下:
//功能:計算兩個正整數的最大公約數
//引數:m,n
//返回:m,n的最大公約數,型別為int整形
#include <stdio.h>
#include <stdlib.h>
int gcd(int m,int n)
{
int i=n;
for (i=n;i>1;i--)//逐個遞減計算,遞減計算不需要將全部數進行計算,當計算停止時就是最大值,若遞增計算需要全部計算
{
if (m%i==0&&n%i==0)
{
break;
}
}
return i;
}
int main()//主函式呼叫函式gcd()
{
int m,n;
scanf("%d%d",&m,&n);
printf("%d",gcd(m,n));
return 0;
}
歐幾里得演算法是學習計算機最初接觸到的最基礎的幾個演算法之一,對初學者而言演算法被看做是解決一類事件的通用方法,只要是滿足事件(或者叫做滿足這種關係)的引數按照演算法步驟就能得到準確結果。其實這樣理解可以說大體意思沒有問題,但是缺少細節約束。
演算法有三大約束條件:
一:有窮性 二:確切性 三:可行性
①有窮性
計算機顧名思義是用於計算的,如果無法計算出最終結果那就沒有意義。當一個程式的步驟有無窮多,我們無法得到一個結果,這對於我們使用計算機的人來說就沒有意義,例如下列程式碼:
for (i=0; ;i++)
{
}
上面程式碼可以得到一個無窮大的數,顯而易見這是沒有意義的。這樣的程式碼段稱為死迴圈,演算法設計中一定避免死迴圈。我們回看歐幾里得演算法,他的有窮性是肯定的,因為任意兩個正整數一定有公因數。
②確切性
這一部分初學者掌握的相對較好,在最初的程式設計中進行練習的大部分是數學問題,數學問題本身就是邏輯嚴密,確切性很好的問題。在日常生活中有些事情有著和演算法一樣的明確的步驟,但是會有一些表示程度的詞彙(倒一些水,走得慢一點),一些,一點,這樣的表示程度的詞語使得最終的結果不會是唯一的,而演算法中唯一的輸入必然對應唯一的輸出。演算法步驟一定是邏輯嚴密,沒有歧義的準確表達。這和計算機語言使用符號化的表示方式是一個道理。
歐幾里得演算法中,三個步驟是確切的,準確的表述
③可行性
計算機世界給我們帶來的一直是快節奏,計算機的優點就是計算快速,所以我們也希望我們的演算法步驟能在較快時間內執行結束。一個演算法有窮,但是計算時間過長也是不被允許的。如果一個演算法需要計算十年或者更長,那我們利用計算機的意義何在。
一個優秀的演算法,不單是能夠計算,能得到結果,他的執行效率也是相當重要的。