由扔骰子看平均概率生成
昨天读到一个帖子,说如何通过扔骰子的方法得到7件事的平均概率情况(扔骰子只能得到6件事的均概率),其中一种解法为,扔两次,分别记为x,y,则定义计算式m=(x-1)*6+y,可得到m的范围为1-36,且m为均概率事件,则剩下要做的就很简单了,去掉36这一项,剩下的mod7即可。
通俗点说若想得到7件事的均概率,则必须能够通过计算得到m件事的均概率且m>=7,(上题m=36),然后再进行取舍。
因此,若想通过骰子得到4件事的均概率,则不需要定义新的计算式,因为掷骰子本身就是6件事的均概率(m=6且6>4),则对掷出的结果去掉5,6两项,剩下的mod4即可。
还有一种通解但是成功概率小很多的情况(本题求7的均概率成功率比较大,因为只有36这一种情况要舍弃),若需要n件事的概率,则先求得n转换为六进制数所需要的位数m,m就是转动骰子的次数(每转一次确定一位),转动m次后得到m个数字,计算得到六进制随机值p,如果p>n,则抛弃这次的投掷,否则认为是成功的投掷,但这样会大大降低得到可用解的概率,所以还需引申公式。
进而推广成为平均概率生成的问题,已知函数randM()可以等概率生成1~m的数字,试编写函数randN(),要求等概率生成1~n的数字
根据以上结论,分两种情况
1、若n<=m,则比较简单,通过取余便可得到(注意去掉多余的项数)
randN()
{
m=randM();
while(m>n)
{
m=randM();
}
return m;
}
2、若n>m,首先计算k,使得m^k>n>m^(k-1),k即为利用原函数的次数(上题即为掷骰子次数),利用掷k次骰子(用k次原函数)可得到m^k次种均概率事件且m^k>n,为方便后面讨论,记m^k=x.此时,依然有两种接法。
法一,生成概率比较低,但思路简答,将生成的x种均概率记为x种不同的情况,取其中的前n种即可(后面的舍弃) 注:此时虽然生成x种不同的均概率,但并非1~x的均概率事件
法二,生成概率较高,但需要一步运算,分别记k次利用randN()得到的数为k1,k2…kk,则通过公式k1+(k2-1)·k+(k3-1)·k^2……得到1~x的均概率,通过此种方法舍弃的情况可以减少,从而大大提高命中率
下面举一个例子,已知rand4(),要求生成1~31的均概率
rand31()
{
int x=rand4();
int y=rand4();
int z=rand4(); //因为4^3>31>4^2,所以原概率要利用3次,即k=3
int m=x+(y-1)*4+(z-1)*64; //则m为1-64的均概率
if(m>62)
重新生成x,y,z,重新计算m;
else
return (m%31)+1;成功得到1-31的均概率,且命中率为31/32
}
解释:虽然此类解法可以生成1-m的均概率,但不能保证概率之和相加为1,因此有命中率一词,生成结果有效,不需要重新生成的概率越大,命中率越高。显然,在同样能实现1-m均概率的各种方法中,命中率应该越高越好。