窮 舉 算 法 (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