模拟掷骰子。以下代码能够计算每种两个骰子之和的准确概率分布:
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/
源码下载链接: