算法思想--窮舉/遞推/遞歸/分治/概率思想

窮舉

最簡單算法,依賴計算機的強大計算能力窮盡每一種可能的情況。窮舉算法效率不高,但是適合一些沒有明顯規律可循的場合。
比如記載於《孫子算經》之中的雞兔同籠問題,適合用窮舉思想解決。

今有雞兔同籠,上有三十五頭,下有九十四足,問雞兔各幾何?

遍歷雞的個數,從0到35個,得到兔的個數,判斷是否滿足條件

int qiongju(int head, int foot, int * chicken, int * rabbit)
{
	int re=0, i, j;
	for (i = 0; i <= head; i++)//遍歷雞爲i個
	{
		j = head - i;//兔的個數
		if (i * 2 + j * 4 == foot)
		{
			re = 1;
			*chicken = i;
			*rabbit = j;
		}
	}
	return re;
}

遞推

遞推根據已知的條件和結果的關係,求解中間結果,根據不斷推進中間結果,求得最後想要的結果。這種條件和結果的關係就如同自變量和因變量一樣,所以遞推思想常用於數學相關領域。
比如斐波拉契數列便適合使用遞推思想來解決

如果一對兩個月大的兔子以後每個月都可以生一對小兔子,而一對新生的兔子出生兩個月後纔可以生兔子。
也就是1月出生到3月才能產崽,那麼假設一年內沒有兔子死亡,一年後共有多少對兔子?

第一個月:1對兔子
第二個月:1對兔子
第三個月:2對兔子
第四個月:3對兔子
第五個月:5對兔子
可以看出,第三月後,N月是之前兩個月兔子的和。
及可以得到因果的公式:F(N)=F(N-1)+F(N-2);

int ditui(int n)//輸入月份,返回當月兔子數量
{
	int i, t;
	if (n == 1 || n == 2)
	{
		return 1;
	}
	else
	{
		i = ditui(n - 1);
		t = ditui(n-2);
		return i + t;
	}
}

遞歸

遞歸算法是再程序中不斷調用自身來達到解決問題的方法,也是適合於數學計算
遞歸調用,可以直接調用自身,也可以先調用其他函數,再通過其他函數調用自身。

求階乘

5!=5* 4 *3 *2 1
N!=N
(N-1)!

int digui(int n)
{
	if (n <= 1)
		return 1;
	else
	{
		return n*digui(n - 1);
	}
}

關於遞推和遞歸的一些區別

遞推:知道第一個,推出下一個,直到達到目的。
遞歸:要知道第一個,需要先知道下一個,直到一個已知的,再反回來,得到上一個,直到第一個。所以遞歸包含遞推和回溯的過程,遞推是遞歸的子集

分治

分而治之,是一種化繁爲簡的思想。
1,將複雜問題劃分爲N個小問題
2,遞歸的求解每個小問題
3,將各個小問題的解合併得到圓問題的解

這裏有30個硬幣,其中一個是假幣,但無法用肉眼區分,只知道假幣比真幣輕一點,如何區分

將所有的硬幣劃分爲兩份,判斷那一份比較輕,再遞推求解,直到只剩下兩個硬幣。

	int coin[30] = { 2,2,2,2,2, 1,2,2,2,2, 2,2,2,2,2, 2,2,2,2,2, 2,2,2,2,2, 2,2,2,2,2 };
	printf("假幣爲第%d個\n", fenzhi(coin,0,29));

int fenzhi(int *coin,int low ,int high)
{
	int i=0, sum1=0, sum2=0, sum3=0,re;
	if (low + 1 == high)//最後兩個硬幣比較
	{
		if (coin[low] < coin[high])
		{
			re = low + 1;
			return re;
		}
		else
		{
			re = high + 1;
			return re;
		}
	}

	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; i<=high;i++)//計算右半部分的重量
		{
			sum2 = sum2 + coin[i];
		}
		if (sum1 > sum2)//左邊重
		{
			re = fenzhi(coin, low + (high - low) / 2 + 1, high);//遞歸右邊的硬幣
				return re;
		}
		else if (sum1 < sum2)//右邊重
		{
			re = fenzhi(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 = fenzhi(coin, low + (high - low) / 2 + 1, high);
			return re;
		}
		else if (sum1 < sum2)//右邊重
		{
			re = fenzhi(coin, low, low + (high - low) / 2-1);
			return re;
		}
		if (sum1 + sum3 == sum2 + sum3)//中間爲假幣
		{
			re = low + (high - low) / 2 + 1;
			return re;
		}
	}
}

概率算法思想

依照概率統計的思想來解決問題,在不能得到精確值的領域使用廣
1,將問題轉換爲相應的幾何圖形,求解的問題往往是圖形中的一部分
2,向幾何圖形中隨機撒點。
3,統計在幾何圖形和問題區域的點數,求得結果。
4,精度不夠則繼續撒點

概率算法的4種形式

1,數值概率算法
2,蒙特卡羅算法
3,拉斯維加斯算法
4,舍伍德算法

蒙特卡羅算法求圓周率

ps:蒙特卡洛是一個地名,位於賭城摩納哥,象徵概率。蒙特卡洛(Monte Carlo)方法是由大名鼎鼎的數學家馮·諾伊曼提出的,誕生於上世紀40年代美國的“曼哈頓計劃”。原理是通過大量隨機樣本,去了解一個系統,進而得到所要計算的值。

圖中一個圓面積 S=PI * R^2=PI
圖中一個正方形面積爲 1
只要在陰影方塊中均勻撒點,只要次數過,那麼點數之比S(圓內) / 方塊=PI/4

double gailv(int n)
{
	double PI,x,y;
	int i, sum=0;
	srand(time(NULL));
	for (i = 1; i < n; i++)
	{
		x = (double)rand() / RAND_MAX;
		y = (double)rand() / RAND_MAX;
		if ((x*x + y*y) <= 1)
		{
			sum++;
		}

	}
	PI = 4.0*sum / n;
	return PI;

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