模擬擲骰子。以下程式碼能夠計算每種兩個骰子之和的準確概率分佈:
int SIDES = 6; double[] dist = new double[2*SIDES+1]; for (int i = 1; i <= SIDES; i++) for (int j = 1; i <= SIDES; j++) dist[i+j] += 1.0; for (int k = 2; k <= 2*SIDES; k++) dist[k] /= 36.0;
dist[i] 的值就是兩個骰子之和為i的概率。用實驗模擬N次擲骰子,並在計算兩個1到
6之間的隨機整數之和時記錄每個值的出現頻率以驗證它們的概率。N要多大才能夠保證你
的經驗資料和準確資料的吻合程度達到小數點後三位?
實驗程式碼:
1 package com.beyond.algs4.experiment; 2 3 import java.math.BigDecimal; 4 5 import com.beyond.algs4.lib.StdRandom; 6 7 8 public class Sides { 9 10 private static int SIDES = 6; 11 12 private double[] dist = new double[2*SIDES + 1]; 13 14 public double[] getDist() { 15 return dist; 16 } 17 18 public void setDist(double[] dist) { 19 this.dist = dist; 20 } 21 22 public void probability() { 23 for (int i = 1; i <= SIDES; i++) { 24 for (int j = 1; j <= SIDES; j++) { 25 dist[i + j] += 1.0; 26 } 27 } 28 for (int k = 2; k <= 2*SIDES; k++) { 29 dist[k] /= 36.0; 30 } 31 } 32 33 public void print() { 34 for (int i = 0; i < dist.length; i++) { 35 System.out.println( 36 String.format("Probability of [%d] is: %f", i, dist[i])); 37 } 38 } 39 40 public static class Emulator { 41 private int N = 100; 42 43 private double[] dist = new double[2*SIDES + 1]; 44 45 public int getN() { 46 return N; 47 } 48 49 public void setN(int n) { 50 N = n; 51 } 52 53 public double[] getDist() { 54 return dist; 55 } 56 57 public void setDist(double[] dist) { 58 this.dist = dist; 59 } 60 61 public void emulator() { 62 for (int i = 0; i < N; i++) { 63 int a = StdRandom.uniform(1, 7); 64 int b = StdRandom.uniform(1, 7); 65 dist[a + b] += 1.0; 66 } 67 for (int k = 2; k <= 2*SIDES; k++) { 68 dist[k] /= N; 69 } 70 } 71 72 public int n(Sides sides) { 73 for (int i = 1; i <= 100; i++) { 74 this.setN(new Double(Math.pow(10, i)).intValue() * this.N); 75 this.emulator(); 76 boolean appr = true; 77 for (int k = 2; k <= 2*SIDES; k++) { 78 double s = this.getDist()[k]; 79 BigDecimal bs = new BigDecimal(s); 80 double s1 = bs.setScale(2, BigDecimal.ROUND_DOWN).doubleValue(); 81 double t = sides.getDist()[k]; 82 BigDecimal bt = new BigDecimal(t); 83 double t1 = bt.setScale(2, BigDecimal.ROUND_DOWN).doubleValue(); 84 if (s1 != t1) { 85 appr = false; 86 break; 87 } 88 } 89 if (appr) { 90 return this.getN(); 91 } 92 } 93 return 0; 94 } 95 96 public void print() { 97 for (int i = 0; i < dist.length; i++) { 98 System.out.println( 99 String.format("Probability of [%d] is: %f", i, dist[i])); 100 } 101 } 102 103 104 } 105 106 public static void main(String[] args) { 107 Sides sides = new Sides(); 108 sides.probability(); 109 110 Emulator e = new Emulator(); 111 int N = e.n(sides); 112 System.out.println(String.format("The N is: %d", N)); 113 System.out.println("Actual: "); 114 sides.print(); 115 System.out.println("Experiment: "); 116 e.print(); 117 } 118 119 }
實驗結果:
1 The N is: 100000000 2 Actual: 3 Probability of [0] is: 0.000000 4 Probability of [1] is: 0.000000 5 Probability of [2] is: 0.027778 6 Probability of [3] is: 0.055556 7 Probability of [4] is: 0.083333 8 Probability of [5] is: 0.111111 9 Probability of [6] is: 0.138889 10 Probability of [7] is: 0.166667 11 Probability of [8] is: 0.138889 12 Probability of [9] is: 0.111111 13 Probability of [10] is: 0.083333 14 Probability of [11] is: 0.055556 15 Probability of [12] is: 0.027778 16 Experiment: 17 Probability of [0] is: 0.000000 18 Probability of [1] is: 0.000000 19 Probability of [2] is: 0.027754 20 Probability of [3] is: 0.055544 21 Probability of [4] is: 0.083374 22 Probability of [5] is: 0.111130 23 Probability of [6] is: 0.138897 24 Probability of [7] is: 0.166751 25 Probability of [8] is: 0.138832 26 Probability of [9] is: 0.111088 27 Probability of [10] is: 0.083306 28 Probability of [11] is: 0.055517 29 Probability of [12] is: 0.027807
結果分析:
多次執行,N值一般為100000000,也有不穩定的時候是其他數值。
補充說明:
1. 以N=100為初始值,迴圈執行計算模擬N次的概率情況,如果吻合度不滿足則N以10倍遞增直至找到合適的N值。(有待改進,採用while(find))
2. “吻合程度達到小數點後三位”的實現方式採用小數點後三位ROUND_DOWN的方式,有待改進
3. N值並非每次執行皆為100000000的穩定結果,可適當隨機多次執行,以分析N值的分佈情況
參考資料:
演算法 第四版 謝路雲 譯 Algorithms Fourth Edition [美] Robert Sedgewick, Kevin Wayne著
http://algs4.cs.princeton.edu/home/
原始碼下載連結: