[算法]Java实现 模拟掷骰子

来源:互联网 发布:mac是哪个国家的品牌 编辑:程序博客网 时间:2024/04/29 09:19

[算法]Java实现 模拟掷骰子

代码说明

1.计算掷两枚骰子所得点数之和的准确概率;
2.计算模拟掷N次所得两枚骰子点数之和的经验概率;
3.当经验概率与准确概率吻合到小数点后三位时输出数据。

完整源码

public class Crap2{    // 计算掷两枚骰子所得点数之和的准确概率    public static double[] Prob()    {        int SIDES = 6;        double[] dist = new double[2*SIDES+1];        for(int i=1;i<=SIDES;i++)            for(int j=1;j<=SIDES;j++)                dist[i+j] += 1.0;        for(int k=2;k<=2*SIDES;k++)        {            dist[k] /= 36.0;        }        return dist;    }    //计算模拟掷N次所得两枚骰子点数之和的经验概率    public static double[] testPro(double N){        int SIDES = 6;        double[] b = new double[2*SIDES+1];        double t = N;        while(N>0){            int m = 1+(int)(Math.random()*6);            int n = 1+(int)(Math.random()*6);            b[m+n] += 1.0;            N--;        }        for(int i=2;i<=SIDES*2;i++){            b[i] /= t;        }        return b;    }    public static void main(String args[])    {        int SIDES =6;        double N = 64;        double[] a = Prob();        double[] b = new double[SIDES*2+1];        int p =0;        while(p==0){            b = testPro(N);            p = 1;            for(int i=2;i<=2*SIDES;i++){            // 反复对比,直到全部情况数据吻合度达到小数点后三位                if(Math.abs(a[i]-b[i])>.0001){                    p = 0;                    break;                }            }            N*=2;        }        //当经验概率与准确概率吻合到小数点后三位时输出数据        System.out.println("Test Times "+N);        for(int i=0;i<=SIDES*2;i++){            System.out.println(i+" "+a[i]+" "+b[i]);        }    }}

输出结果(模拟)

Compiling Crap2.java.......  -----------OUTPUT-----------  Test Times 6.7108864E70 0.0 0.01 0.0 0.02 0.027777777777777776 0.0278392136096954353 0.05555555555555555 0.055485188961029054 0.08333333333333333 0.083392083644866945 0.1111111111111111 0.111125588417053226 0.1388888888888889 0.13884845376014717 0.16666666666666666 0.16670277714729318 0.1388888888888889 0.138930976390838629 0.1111111111111111 0.1111281514167785610 0.08333333333333333 0.0832940042018890411 0.05555555555555555 0.055494755506515512 0.027777777777777776 0.027758806943893433[Finished in 4.3s]

代码解释

  • 关于存放概率的数组长度int SIDES = 6; double[] dist = new double[2*SIDES+1];
    ,骰子有六面,掷两枚可以依次掷出总和为{2,3,4,5,6,7,8,9,10,11,12}
    等一共11种结果,数组下标从0 开始标记,所以数组的长度在声明时需要声明成11+2 = 6*2+1 =
    SIDES*2+1
    一共13的长度;
  • 换个更简单的思路可以这么想,物理情况下总和最低是1+1=2,最高是6+6=12,存放12本质上是数据的第13个数据,因此声明长度为13的数组;
    不用考虑具体的某一种情况的总和,因为i+j总和可以出现的情况是有穷的
for(int i=1;i<=SIDES;i++)            for(int j=1;j<=SIDES;j++)                dist[i+j] += 1.0;
  • 既然和的情况是有穷的,那么移植到模拟实验的部分,每次随机出来的两个数字m n,也就可以使用m+n将得到的和次数填充到double[] b = new double[2*SIDES+1]; b[m+n] += 1.0;数组里;
  • 然后是测试部分,N控制着生成一个经验概率数组b[]所要进行地从[1,6]中随机取单个整数的次数,起始值设定为64(2^6),没什么特殊意义想要一个大于36的2的倍数而已;
  • 反复对比,N*=2直到全部情况数据经验概率准确概率吻合到小数点后三位时输出,其中p是用来控制循环的变量,当两种概率的吻合度并没有达到小数点后三位时,p始终置为0保证循环继续使N增大,N增大物理意义上就使得模拟掷骰子的次数上升了;直到全部情况的吻合度达到要求,p置1跳出循环;
int p =0;        while(p==0){            b = testPro(N);            p = 1;            for(int i=2;i<=2*SIDES;i++){            // 反复对比,直到全部情况数据吻合度达到小数点后三位                if(Math.abs(a[i]-b[i])>.0001){                    p = 0;                    break;                }            }            N*=2;        }
  • 吻合度达到小数点后三位的判断使用if(Math.abs(a[i]-b[i])>.0001){},计算两个数值差的绝对值,当这个绝对值小于.0001即表示吻合;

代码心得

  1. 数组长度那里一开始我以为2*SIDES就足够了,一开始没有想到0和1是不会出现的,下标即和这种思路赋予了数组下标丰富的物理意义,很巧妙以后也要多利用,用白话说起来就是数组下标是9的位置存着的值就是9这个数字出现的次数
  2. 同时,模拟测试部分,不断增加N的大小使用P来控制循环也是经典套路了,真是很好用啊!我觉得按照这种思路比较容易把握p的变化时刻以及循环内外的赋值,也就是最好从里往外写;
  3. 参考下面的代码,最里面的if是循环结束的控制条件,加了break表示的是这种情况只要发生一次我就不需要继续我的外层for循环了,这里的for循环是为了将全部的数据进行吻合度的匹配,如果已经出现了不符合条件的就不需要遍历剩余的元素了;因此,既然直到目前为止还没有出现符合全部吻合度的N,那么外层的while循环就必须继续….
int p =0; while(p==0){            p = 1;                for(){                        if(){                            p = 0;                            break;                        }            }        }
0 0
原创粉丝点击