n個骰子的點數

題目:把n個骰子扔在地上,所有骰子朝上一面的點數之和爲S。輸入n,打印出S的所有可能的值出現的概率。

分析:骰子一共6個面,每個面上都有一個點數,對應的數字是1到 6之間的一個數字。所以,n個骰子的點數和的最小值爲n,最大值爲6n。因此,一個直觀的思路就是定義一個長度爲6n-n的數組,和爲S的點數出現的次數保存到數組第S-n個元素裏。另外,我們還知道n個骰子的所有點數的排列數6^n。一旦我們統計出每一點數出現的次數之後,因此只要把每一點數出現的次數除以n^6,就得到了對應的概率。

該思路的關鍵就是統計每一點數出現的次數。要求出n個骰子的點數和,我們可以先把n個骰子分爲兩堆:第一堆只有一個,另一個有n-1個。單獨的那一個有可能出現從1到6的點數。我們需要計算從1到6的每一種點數和剩下的n-1個骰子來計算點數和。接下來把剩下的n-1個骰子還是分成兩堆,第一堆只有一個,第二堆有n-2個。我們把上一輪那個單獨骰子的點數和這一輪單獨骰子的點數相加,再和剩下的n-2個骰子來計算點數和。分析到這裏,我們不難發現,這是一種遞歸的思路。遞歸結束的條件就是最後只剩下一個骰子了。

基於這種思路,我們可以寫出如下代碼:

int g_maxValue=6;

void SubProbabilityofDices(int original, int current, int value, int tempSum, int *pProbalities)
{
	if(current==1)
	{
		int sum=value+tempSum;
		pProbalities[sum-original]++;
	}
	else
	{
		for(int i=1; i<=g_maxValue; i++)
		{
			int sum=value+tempSum;
			SubProbabilityofDices(original, current-1, i, sum, pProbalities);
		}
	}
}

void SumProbabilityofDices(int number, int* pProbabilities)
{
	for(int i=1; i<=g_maxValue; i++)//第一個骰子
		SubProbabilityofDices(number, number, i, 0, pProbabilities);

}

void PrintSumProbabilityofDices_1(int number)
{
	if(number<1)
		return;

	int maxSum=number*g_maxValue;
	int* pProbabilities=new int [maxSum-number+1];
	for(int i=number; i<=maxSum; i++)
		pProbabilities[i-number]=0;

	SumProbabilityofDices(number, pProbabilities);

	int total=pow((double)g_maxValue, number);
	for(i=number; i<=maxSum; i++)
	{
		float ratio=(float)pProbabilities[i-number]/total;
		cout<<i<<":"<<ratio<<endl;
	}
	delete[] pProbabilities;
}


上述算法當number比較小的時候表現很優異。但由於該算法基於遞歸,它有很多計算是重複的,從而導致當number變大時性能讓人不能接受。

爲了避免遞歸時的重複計算,我們很自然聯想到動態規劃(DP)。用表格法,一行代表一個骰子,列表示各個S值,所以一共有6*N列。本來是要用N行的,可是這裏只用了一個二維的數組,因爲現在計算的值只與前一次計算的值相關,所以其中一行保存上一次計算的結果,另一行保存正在計算的結果,這樣可以節省大量的空間。我們可以考慮用兩個數組來存儲骰子點數每一總數出現的次數。在一次循環中,第一個數組中的第n個數字表示骰子和爲n出現的次數。那麼在下一循環中,我們加上一個新的骰子。那麼此時和爲n的骰子出現的次數,應該等於上一次循環中骰子點數和爲n-1、n-2、n-3、n-4、n-5與n-6的總和。所以我們把另一個數組的第n個數字設爲前一個數組對應的第n-1、n-2、n-3、n-4、n-5與n-6之和。

void PrintSumProbabilityofDices_2(int number)
{
	double* pProbabilities[2];
	pProbabilities[0]=new double[number*g_maxValue+1];
	pProbabilities[1]=new double[number*g_maxValue+1];

	for(int i=0; i<number*g_maxValue+1; i++)
	{
		pProbabilities[0][i]=0;
		pProbabilities[1][i]=0;
	}

	int flag=0;
	for(i=1; i<=g_maxValue; i++)
		pProbabilities[flag][i]=1;

	for(int k=2; k<=number; k++)
	{
		for(int i=k; i<=g_maxValue*k; i++)
		{
			pProbabilities[1-flag][i]=0;
			for(int j=1; j<=i && j<=g_maxValue; j++)
				pProbabilities[1-flag][i]+=pProbabilities[flag][i-j];
		}
		flag=1-flag;
	}

	double total=pow((double)g_maxValue, number);

	for(i=number; i<=g_maxValue*number; i++)
	{
		double ratio=pProbabilities[flag][i]/total;
		cout<<i<<":"<<ratio<<endl;
	}

	delete[] pProbabilities[0];
	delete[] pProbabilities[1];
}


感謝:http://www.36qp.com/news/jinyan/201201/62824.shtml

發佈了90 篇原創文章 · 獲贊 22 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章