【Java】 劍指offer(60) n個骰子的點數

 

本文參考自《劍指offer》一書,代碼採用Java語言。html

更多:《劍指Offer》Java實現合集  java

題目

  把n個骰子扔在地上,全部骰子朝上一面的點數之和爲s。輸入n,打印出s的全部可能的值出現的機率。git

思路

  對於n個骰子,要計算出每種點數和的機率,咱們知道投擲n個骰子的總狀況一共有6^n種,所以只須要計算出某點數和的狀況一共有幾種,便可求出該點數之和的機率。編程

  方法一:基於遞歸的方法,效率較低數組

  易知,點數之和s的最小值爲n,最大值爲6*n,所以咱們考慮用一個大小爲(6*n-n+1)的數組存放不一樣點數之和的狀況個數,那麼,若是點數之和爲x,那麼把它出現的狀況總次數放入數組種下標爲x-n的元素裏。ide

  肯定如何存放不一樣點數之和的次數後,咱們要計算出這些次數。咱們把n個骰子分爲1個骰子和n-1個骰子,這1post

個骰子可能出現1~6個點數,由該骰子的點數與後面n-1個骰子的點數能夠計算出總點數;然後面的n-1個骰子又能夠分爲1個和n-2個,把上次的點數,與如今這個骰子的點數相加,再和剩下的n-2個骰子的點數相加能夠獲得總點數……,便可以用遞歸實現。在得到最後一個骰子的點數後能夠計算出幾個骰子的總點數,令數組中該總點數的狀況次數+1,便可結束遍歷。性能

  方法二:基於循環求骰子點數,時間性能好測試

  用數組存放每種骰子點數和出現的次數。令數組中下標爲n的元素存放點數和爲n的次數。咱們設置循環,每一個循環多投擲一個骰子,假設某一輪循環中,咱們已知了各類點數和出現的次數;在下一輪循環時,咱們新投擲了一個骰子,那麼此時點數和爲n的狀況出現的次數就等於上一輪點數和爲n-1,n-2,n-3,n-4,n-5,n-6的狀況出現次數的總和。從第一個骰子開始,循環n次,就能夠求得第n個骰子時各類點數和出現的次數。編碼

  咱們這裏用兩個數組來分別存放本輪循環與下一輪循環的各類點數和出現的次數,不斷交替使用。

 

測試算例 

  1.功能測試(1,2,3,4個骰子)

  2.特殊測試(0個)

  3.性能測試(11個)

Java代碼

import java.text.NumberFormat;

//題目:把n個骰子扔在地上,全部骰子朝上一面的點數之和爲s。輸入n,打印出s
//的全部可能的值出現的機率。

public class DicesProbability {
	

	private static final int maxValue = 6; 

	/**
	 * 方法一:遞歸解法
	 */
	public static void printProbability1(int number) {
		if(number<=0)
			return;  //錯誤
		int[] probabilities = new int[maxValue*number-number+1]; 
		//下標爲i,對應的值表明點數之和爲i+number總共出現的狀況次數
		//點數從number~maxValue*number,因此數組大小爲6*number-number+1
		for(int i=0;i<probabilities.length;i++)
			probabilities[i]=0;
		
		//計算不一樣點數出現的次數
		for(int i=1;i<=maxValue;i++)
			calP(probabilities,number,number-1,i);  //第一次擲骰子,總點數只能是1~maxValue(即6)
		
		int totalP = (int) Math.pow(maxValue, number);  //全部狀況總共出現的次數 
		for( int i=0;i<probabilities.length ;i++) {
			double ratio = (double)probabilities[i]/totalP;
			NumberFormat format = NumberFormat.getPercentInstance();
			format.setMaximumFractionDigits(2);//設置保留幾位小數
			System.out.println("點數和爲"+(i+number)+"的機率爲:"+format.format(ratio));
		}
	}
	
	/**
	 * 計算每種點數出現的次數
	 * @param number:骰子總個數
	 * @param curNumber:當前剩餘骰子個數
	 * @param sum:各個骰子加起來的總點數
	 */
	private static void calP(int[] probabilities, int number, int curNumber, int sum) {
		if(curNumber==0) {
			probabilities[sum-number]++; //總數爲sum的狀況存放在sum-number下標中
			return;
		}
		for(int i=1;i<=maxValue;i++)
			calP(probabilities, number, curNumber-1, sum+i);  //至關於剩餘的骰子少一個,總點數增長。
	}
	
	//===========================================
	/**
	 * 方法二:基於循環求骰子點數,時間性能好
	 */
	public static void printProbability2(int number) {
		if(number<=0)
			return;  //錯誤
		int[][] probabilities = new int[2][number*maxValue+1];
		//[2]表明用兩個數組交替保存,[number*maxValue+1]是指點數爲所在下標時,該點數出現的總次數。
		//probabilities[*][0]是沒用的,只是爲了讓下標對應點數
		for(int i=0;i<2;i++) {
			for(int j=0;j<number*maxValue;j++) {
				probabilities[i][j]=0;
			}
		}
		
		for(int i=1;i<=6;i++)
			probabilities[0][i]=1;  //第一個骰子出現的狀況
		
		int flag=0;
		for(int curNumber=2;curNumber<=number;curNumber++) {   //當前是第幾個骰子
			for(int i=0;i<curNumber;i++)
				probabilities[1-flag][i]=0;  //前面的數據清零
			
			for(int i=curNumber;i<=curNumber*maxValue;i++) {
				for(int j=1;j<=6 && j<=i ;j++) {
					probabilities[1-flag][i]+=probabilities[flag][i-j];
				}
			}
			flag=1-flag;
			
		}
		
		int totalP = (int) Math.pow(maxValue, number);  //全部狀況總共出現的次數 
		for( int i=number;i<= number*6;i++) {
			double ratio = (double)probabilities[flag][i]/totalP;
			NumberFormat format = NumberFormat.getPercentInstance();
			format.setMaximumFractionDigits(8);//設置保留幾位小數
			System.out.println("點數和爲"+(i+number)+"的機率爲:"+format.format(ratio));
		}
	}
	
	
	public static void main(String[] args) {
		System.out.println("=========方法一============");
		for(int i=0;i<=3;i++) {
			System.out.println("-----骰子數爲"+i+"時-----");
			printProbability1(i);
		}
		System.out.println("-----骰子數爲"+11+"時-----");
		printProbability1(11);
		
		System.out.println("=========方法二============");
		for(int i=0;i<=3;i++) {
			System.out.println("-----骰子數爲"+i+"時-----");
			printProbability2(i);
		}
		System.out.println("-----骰子數爲"+11+"時-----");
		printProbability1(11);
	}
}

  

=========方法一============
-----骰子數爲0時-----
-----骰子數爲1時-----
點數和爲1的機率爲:16.66666667%
點數和爲2的機率爲:16.66666667%
點數和爲3的機率爲:16.66666667%
點數和爲4的機率爲:16.66666667%
點數和爲5的機率爲:16.66666667%
點數和爲6的機率爲:16.66666667%
-----骰子數爲2時-----
點數和爲2的機率爲:2.77777778%
點數和爲3的機率爲:5.55555556%
點數和爲4的機率爲:8.33333333%
點數和爲5的機率爲:11.11111111%
點數和爲6的機率爲:13.88888889%
點數和爲7的機率爲:16.66666667%
點數和爲8的機率爲:13.88888889%
點數和爲9的機率爲:11.11111111%
點數和爲10的機率爲:8.33333333%
點數和爲11的機率爲:5.55555556%
點數和爲12的機率爲:2.77777778%
-----骰子數爲3時-----
點數和爲3的機率爲:0.46296296%
點數和爲4的機率爲:1.38888889%
點數和爲5的機率爲:2.77777778%
點數和爲6的機率爲:4.62962963%
點數和爲7的機率爲:6.94444444%
點數和爲8的機率爲:9.72222222%
點數和爲9的機率爲:11.57407407%
點數和爲10的機率爲:12.5%
點數和爲11的機率爲:12.5%
點數和爲12的機率爲:11.57407407%
點數和爲13的機率爲:9.72222222%
點數和爲14的機率爲:6.94444444%
點數和爲15的機率爲:4.62962963%
點數和爲16的機率爲:2.77777778%
點數和爲17的機率爲:1.38888889%
點數和爲18的機率爲:0.46296296%
-----骰子數爲11時-----
點數和爲11的機率爲:0.00000028%
點數和爲12的機率爲:0.00000303%
點數和爲13的機率爲:0.00001819%
點數和爲14的機率爲:0.00007883%
點數和爲15的機率爲:0.00027591%
點數和爲16的機率爲:0.00082774%
點數和爲17的機率爲:0.00220426%
點數和爲18的機率爲:0.00532722%
點數和爲19的機率爲:0.01186118%
點數和爲20的機率爲:0.02459557%
點數和爲21的機率爲:0.04789041%
點數和爲22的機率爲:0.08811621%
點數和爲23的機率爲:0.15397396%
點數和爲24的機率爲:0.25654646%
點數和爲25的機率爲:0.40891953%
點數和爲26的機率爲:0.6252344%
點數和爲27的機率爲:0.91910173%
點數和爲28的機率爲:1.30143669%
點數和爲29的機率爲:1.77793036%
點數和爲30的機率爲:2.34652097%
點數和爲31的機率爲:2.99533825%
點數和爲32的機率爲:3.70163009%
點數和爲33的機率爲:4.43211149%
點數和爲34的機率爲:5.14496733%
點數和爲35的機率爲:5.79345109%
點數和爲36的機率爲:6.33070903%
點數和爲37的機率爲:6.71518156%
點數和爲38的機率爲:6.91574824%
點數和爲39的機率爲:6.91574824%
點數和爲40的機率爲:6.71518156%
點數和爲41的機率爲:6.33070903%
點數和爲42的機率爲:5.79345109%
點數和爲43的機率爲:5.14496733%
點數和爲44的機率爲:4.43211149%
點數和爲45的機率爲:3.70163009%
點數和爲46的機率爲:2.99533825%
點數和爲47的機率爲:2.34652097%
點數和爲48的機率爲:1.77793036%
點數和爲49的機率爲:1.30143669%
點數和爲50的機率爲:0.91910173%
點數和爲51的機率爲:0.6252344%
點數和爲52的機率爲:0.40891953%
點數和爲53的機率爲:0.25654646%
點數和爲54的機率爲:0.15397396%
點數和爲55的機率爲:0.08811621%
點數和爲56的機率爲:0.04789041%
點數和爲57的機率爲:0.02459557%
點數和爲58的機率爲:0.01186118%
點數和爲59的機率爲:0.00532722%
點數和爲60的機率爲:0.00220426%
點數和爲61的機率爲:0.00082774%
點數和爲62的機率爲:0.00027591%
點數和爲63的機率爲:0.00007883%
點數和爲64的機率爲:0.00001819%
點數和爲65的機率爲:0.00000303%
點數和爲66的機率爲:0.00000028%
=========方法二============
-----骰子數爲0時-----
-----骰子數爲1時-----
點數和爲2的機率爲:16.66666667%
點數和爲3的機率爲:16.66666667%
點數和爲4的機率爲:16.66666667%
點數和爲5的機率爲:16.66666667%
點數和爲6的機率爲:16.66666667%
點數和爲7的機率爲:16.66666667%
-----骰子數爲2時-----
點數和爲4的機率爲:2.77777778%
點數和爲5的機率爲:5.55555556%
點數和爲6的機率爲:8.33333333%
點數和爲7的機率爲:11.11111111%
點數和爲8的機率爲:13.88888889%
點數和爲9的機率爲:16.66666667%
點數和爲10的機率爲:13.88888889%
點數和爲11的機率爲:11.11111111%
點數和爲12的機率爲:8.33333333%
點數和爲13的機率爲:5.55555556%
點數和爲14的機率爲:2.77777778%
-----骰子數爲3時-----
點數和爲6的機率爲:0.92592593%
點數和爲7的機率爲:1.85185185%
點數和爲8的機率爲:3.24074074%
點數和爲9的機率爲:5.09259259%
點數和爲10的機率爲:6.94444444%
點數和爲11的機率爲:9.72222222%
點數和爲12的機率爲:11.57407407%
點數和爲13的機率爲:12.5%
點數和爲14的機率爲:12.5%
點數和爲15的機率爲:11.57407407%
點數和爲16的機率爲:9.72222222%
點數和爲17的機率爲:6.94444444%
點數和爲18的機率爲:4.62962963%
點數和爲19的機率爲:2.77777778%
點數和爲20的機率爲:1.38888889%
點數和爲21的機率爲:0.46296296%
-----骰子數爲11時-----
點數和爲22的機率爲:0.00121693%
點數和爲23的機率爲:0.00298376%
點數和爲24的機率爲:0.00638621%
點數和爲25的機率爲:0.01258472%
點數和爲26的機率爲:0.02324523%
點數和爲27的機率爲:0.04090248%
點數和爲28的機率爲:0.06852398%
點數和爲29的機率爲:0.11056181%
點數和爲30的機率爲:0.17250802%
點數和爲31的機率爲:0.26102196%
點數和爲32的機率爲:0.38391023%
點數和爲33的機率爲:0.54975226%
點數和爲34的機率爲:0.76760849%
點數和爲35的機率爲:1.04620281%
點數和爲36的機率爲:1.39300386%
點數和爲37的機率爲:1.81311477%
點數和爲38的機率爲:2.30801735%
點數和爲39的機率爲:2.87442713%
點數和爲40的機率爲:3.50323763%
點數和爲41的機率爲:4.17879659%
點數和爲42的機率爲:4.87880723%
點數和爲43的機率爲:5.57487572%
點數和爲44的機率爲:6.23383532%
點數和爲45的機率爲:6.8198307%
點數和爲46的機率爲:7.29713005%
點數和爲47的機率爲:7.63343598%
點數和爲48的機率爲:7.80322374%
點數和爲49的機率爲:7.79077491%
點數和爲50的機率爲:7.59241029%
點數和爲51的機率爲:7.21750041%
點數和爲52的機率爲:6.68797654%
點數和爲53的機率爲:6.03632186%
點數和爲54的機率爲:5.3023148%
點數和爲55的機率爲:4.52891823%
點數和爲56的機率爲:3.75794284%
點數和爲57的機率爲:3.02613646%
點數和爲58的機率爲:2.3622405%
點數和爲59的機率爲:1.78534552%
點數和爲60的機率爲:1.3046269%
點數和爲61的機率爲:0.92032941%
點數和爲62的機率爲:0.62564372%
點數和爲63的機率爲:0.40903116%
點數和爲64的機率爲:0.25656879%
點數和爲65的機率爲:0.15397644%
點數和爲66的機率爲:0.08811621%
點數和爲67的機率爲:0.04789041%
點數和爲68的機率爲:0.02459557%
點數和爲69的機率爲:0.01186118%
點數和爲70的機率爲:0.00532722%
點數和爲71的機率爲:0.00220426%
點數和爲72的機率爲:0.00082774%
點數和爲73的機率爲:0.00027591%
點數和爲74的機率爲:0.00007883%
點數和爲75的機率爲:0.00001819%
點數和爲76的機率爲:0.00000303%
點數和爲77的機率爲:0.00000028%
DicesProbability

 

收穫

  1.int類型相除,要獲得double類型,須要提早將其中一個變成double類型

   例如:double ratio = (double)probabilities[i]/totalP;

  2.輸出百分數的方法,利用NumberFormat

			NumberFormat format = NumberFormat.getPercentInstance();
			format.setMaximumFractionDigits(8);//設置保留幾位小數
			System.out.println("點數和爲"+(i+number)+"的機率爲:"+format.format(ratio));

  3.第二種方法,不是骰子點數的角度出發,而是從點數之和出發,點數之和有:f(n)=f(n-1)+……f(n-6),很是巧妙。

  4.用兩個數組交替存放,學會使用變量flag,flag=1-flag。

  5.代碼中沒有把骰子的最大點數硬編碼爲6,而是用變量maxValue來表示,具備可拓展性。之後本身編程時也要注意這些量是否能夠不用硬編碼,從而提升擴展性。

  6.提升數學建模能力,無論採起哪一種思路,都要先想到用數組來存放n個骰子的每一個點數和出現的次數。

 

  

  

更多:《劍指Offer》Java實現合集 

相關文章
相關標籤/搜索