五種基本算法思想

1.窮舉算法思想

窮 舉 算 法 (ExhaustiveA ttack method)是 最 簡 單 的 一 種 算 法 ,其依賴於計算機的強大計算能力來 窮 盡 每 一 種 可 能 的 情 況 ,從 而 達 到 求 解 問 題 的 目 的 。窮 舉 算 法 效 率 並 不 高 ,但是適應於一 些沒有明 顯 規 律 可 循 的 場 合。

基本算法思想
窮舉算法的基本思想就是從所有可能的情況中搜索正確的答案,其執行步驟如下:
(1)對於一種可能的情況,計算其結果。
(2) 判斷結果是否滿足要求,如果不滿足則執行第(1 ) 步來搜索下一個可能的情況;如果滿足要求,則表示尋找到一個正確的答案。

【注意】在使用窮舉算法時,需要明確問題的答案的範圍,這樣纔可以在指定範圍內搜索答案。指定範圍之後,就可以使用循環語句和條件判斷語句逐步驗證候選答案的正確性,從而得到需要的正確答案。

經典例子
《孫子算經》【雞兔同籠問題】
今有雞兔同籠,上有三十五頭,下有九十四足,問雞兔各幾何?
(在一個籠子裏關着若干只雞和若干只兔,從上面數共有35個頭;從下面數共有94只腳。問籠中雞和兔的數量各是多少?)

int chickenRabbit(int head,int foot,int *c,int *r){
int i,j;
int tag=0;//標誌是否有解
for(i=0;i<=head;i++){//雞的個數窮舉
    j=head-i;//兔子的個數
    if(i*2+j*4==foot){//判斷是否滿足條件
        tag=1;
        *c=i;
        *r=j;
    }
}
return tag;
}
int main()
{
   int c,r;
    if(chickenRabbit(35,94,&c,&r)==1){//如果有解輸出
        printf("chickens=%d,rabbits=%d\n",c,r);
    }else{//如果無解
    printf("無解\n");
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

這裏寫圖片描述

2.遞推算法思想

遞推算法是非常常用的算法思想,在數學計算等場合有着廣泛的應用。遞推算法適合有明顯公式規律的場合。

基本算法思想
遞推算法是一種理性思維模式的代表,根據已有的數據和關係,逐步推導而得到結果。遞推算法的執行過程如下:
(1) 根據已知結果和關係,求解中間結果。
(2)判定是否達到要求,如果沒有達到,則繼續根據已知結果和關係求解中間結果。如果滿足要求,則表示尋找到一個正確的答案。

【注意】遞推算法需要用戶知道答案和問題之間的邏輯關係。在許多數學問題中,都有明確的計算公式可以遵循,因此可以採用遞推算法來實現。

經典例子
斐波那契《算盤書》【兔子產仔問題】
如果一對兩個月大的兔子以後每一個月都可以生一對小兔子,而一對新生的兔子出生兩個月後纔可以生小兔子。也就是說,1 月份出生,3 月份纔可產仔。那麼假定一年內沒有產生兔子死亡事件,那 麼 1年後共有多少對兔子呢?

【規律分析】
第一個月:a(1)=1對兔子;
第二個月:a(2)=1對兔子;
第三個月:a(3)=1+1=2 對兔子;
第四個月:a(4)=1+2=3 對兔子;
第五個月:a(5)=2+3=5 對兔子;
……
第n個月:a(n)=a(n-1)+a(n-2)對兔子;

int Fibonacci(int n)
{
int tl,t2;
if (n==1||n==2)//第1、2個月都只有1對兔子
{
return 1;
}else{//採用遞歸
tl=Fibonacci(n-1);
t2=Fibonacci(n-2);
return tl+t2;//計算第n個月的兔子對數
}
}
int main()
{
   int n=12;
   printf("%d個月後兔子對數:%d\n",n,Fibonacci(n));
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

這裏寫圖片描述

3.遞歸算法思想

遞歸算法是非常常用的算法思想。使用遞歸算法,往往可以簡化代碼編寫,提高程序的可讀性。但是,不合適的遞歸會導致程序的執行效率變低。

基本算法思想
遞歸算法就是在程序中不斷反覆調用自身來達到求解問題的方法。這裏的重點是調用自身,這就要求待求解的問題能夠分解爲相同問題的一個子問題。這樣 ,通過多次遞歸調用,便可以完成求解。
遞歸調用是一個函數在它的函數體內調用它自身的函數調用方式,這種函數也稱爲“遞歸函數”。在遞歸函數中,主調函數又是被調函數。執行遞歸函數將反覆調用其自身,每調用一次就進入新的一層。
函數的遞歸調用分兩種情況:直接遞歸和間接遞歸。
• 直接遞歸:即在函數中調用函數本身。
• 間接遞歸:即間接地調用一個函數,如出如func_a調 用 func_b, func_b 又調用func_a。間接遞歸用得不多。

【注意】編寫遞歸函數時,必須使用if語句強制函數在未執行遞歸調用前返回。否則,在調用函數後,它永遠不會返回,這是一個很容易犯的錯誤。

經典例子
【階乘】

n!=n*(n-1)*(n-2)*……*2*1
  • 1
long fact(int n){
if(n<=1)return 1;
else
    return n*fact(n-1);
}
int main()
{
   int n=11;
   printf("%d的階乘是%d\n",n,fact(n));
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這裏寫圖片描述

4.分治算法思想

分治算法是一種化繁爲簡的算法思想。分治算法往往應用於計算步驟比較複雜的問題,通過將問題簡化而逐步得到結果。

基本算法思想
分治算法的基本思想是將一個計算複雜的問題分爲規模較小,計算簡單的小問題求解,然後綜合各個小問題,得到最終問題的答案。分治算法的執行過程如下:
(1)對於一個規模爲N 的問題,若該問題可以容易地解決(比如說規模>^較小),則直接解決,否則執行下面的步驟。
(2)將該問題分解爲” 個規模較小的子問題,這些子問題互相獨立,並且原問題形式相同。
(3)遞歸地解子問題。
(4)然後,將各子問題的解合併得到原問題的解。

【注意】使用分治算法需要待求解問題能夠化簡爲若干個小規模的相同問題,通過逐步劃分,達到一個易於求解的階段而直接進行求解。然後,程序中可以使用遞歸算法來進行求解。

經典例子
【尋找假幣問題】
一個袋子裏有30個硬幣,其中一枚是假幣,並且假幣和真幣一模- 樣,肉眼很難分辨,目前只知道假幣比真幣重量輕一點。請問如何區分出假幣?

int falseCoin(int coin[],int low,int high){
int i,sum1,sum2,sum3;
int re;
sum1=sum2=sum3=0;
//若只有兩個硬幣
if(low+1==high){
    if(coin[low]<coin[high]){//第一個硬幣是假幣
    re=low+1;
    return re;
}else{//第二個硬幣是假幣
re=high+1;
return re;
}
}
//硬幣個數大於2
if((high-low+1)%2==0){//偶數個硬幣
    for(i=low;i<=low+(high-low)/2;i++){//前半段重量
        sum1=sum1+coin[i];
    }
    for(i=low+(high-low)/2+1;i<=high;i++){//後半段重量
      sum2=sum2+coin[i];
    }
    if(sum1>sum2){//後半段輕,假幣在後半段
        re=falseCoin(coin,low+(high-low)/2+1,high);
    return re;
    }else if(sum1<sum2){//前半段輕,假幣在前半段
     re=falseCoin(coin,low,low+(high-low)/2);
    return re;
    }
}else{//奇數個硬幣
for(i=low;i<=low+(high-low)/2-1;i++){//前半段重量
        sum1=sum1+coin[i];
    }
    for(i=low+(high-low)/2+1;i<=high;i++){//後半段重量
      sum2=sum2+coin[i];
    }
    sum3=coin[low+(high-low)/2];
    if(sum1>sum2){//後半段輕,假幣在後半段
        re=falseCoin(coin,low+(high-low)/2+1,high);
    return re;
    }else if(sum1<sum2){//前半段輕,假幣在前半段
     re=falseCoin(coin,low,low+(high-low)/2-1);
    return re;
    }
    if(sum1+sum3==sum2+sum3){//前半段+中間硬幣和後半段+中間硬幣重量相等,說明中間硬幣是假幣
        re=low+(high-low)/2+1;
        return  re;
    }

}
}
int main()
{
   int n=11,i;
   int a[n];
   for(i=0;i<n;i++){
    a[i]=2;
   }
   a[7]=1;//設置第八個爲假幣
  printf("假幣是第%d個\n", falseCoin(a,0,n-1));
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

這裏寫圖片描述

5.概率算法思想

概率算法依照概率統計的思路來求解問題,往往不能得到問題的精確解,但卻在數值計算領域得到了廣泛的應用。因爲很多數學問題,往往沒有或者很難計算解析解,這時便需要通過數值計算來求解近似值。

基本算法思想
概率算法執行的基本過程如下:
(1)將問題轉化爲相應的幾何圖形S, S 的面積是容易計算的,問題的結果往往對應幾何圖形中某一部分S1 的面積。
(2)然後,向幾何圖形中隨機撒點。
(3)統計幾何圖形S 和 S1 中的點數。根 據 S 的面積和S1 面積的關係以及各圖形中的點數來計算得到結果。
(4) 判斷上述結果是否在需要的精度之內,如果未達到精度則執行步驟(2)。如果達到精度,則輸出近似結果。
概率算法大致分爲如下4 種形式。

• 數值概率算法。
• 蒙 特 卡 羅 (MonteCarlo)算法。
• 拉 斯 維 加 斯 (Las Vegas)算法。
• 舍 伍 德 (Sherwood)算法。

經典例子
【蒙特卡羅PI概率算法問題】
在邊長爲1的正方形內,以1爲半徑畫一個1/4圓。落入院內的概率爲PI/4?
算法思想:在某面積範圍內撒點足夠多,落在固定區域的點的概率就會將近結果。
關鍵:均勻撒點、區域判斷

double MontePI(int n){
double PI;
double x,y;
int i,sum;
sum=0;
srand(time(NULL));
for(i=1;i<n;i++){
    x=(double)rand()/RAND_MAX;//在0-1之間產生一個隨機數x
     y=(double)rand()/RAND_MAX;//在0-1之間產生一個隨機數y
    if((x*x+y*y)<=1){//判斷點是否在圓內
        sum++;//計數
    }
}
    PI=4.0*sum/n;//計算PI
    return PI;
}

int main()
{
   int n=500000;
   double PI;

  printf("蒙特卡羅概率PI=%f\n", MontePI(n));
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章