概率相关题目

1,如何生成概率

主要参考大牛的博客:http://www.cnblogs.com/miloyip/archive/2010/04/21/1717109.html

问题定义

游戏(和一些模拟程序)经常需要使用随机数,去应付不同的游戏(或商业)逻辑。本文分析一个常见问题:有N类物件,设第i类物件的出现概率为P(X=i),如何产生这样的随机变量X?

例如对概率的要求是

P(X=0)=0.12
P(X=1)=0.4
P(X=2)=0.4
P(X=3)=0.07
P(X=4)=0.01

输入数组<0.12, 0.4, 0.4, 0.07, 0.01> 输出符合以上概率的随机数序列,如<1, 4, 2, 1, 2, 2, 1, 0, ...> 。

以下先谈一些统计学背景知识,再给这问题的可行解法。

概率分布

这问题要产生一个随机变量,接近指定的概率分布(probability distribution)。大部份程序语言都提供接近均匀分布(uniformly distributed)的伪随机数产生器(pseudorandom number generator, PRNG),例如JavaScript提供的Math.random()函数,可传回 半开区间的均匀分布伪随机数。

密度分布函数

现在,不仿测试一下JavaScript的Math.random()函数,看看它是否均匀分布。一个变数的分布以密度分布函数(probability density function, PDF)定义,一般写作,随机变量在区间上的概率为定积分:

为了把PDF视觉化,可以把X分为若干区间,统计各区间X出现的频率,绘画其直方图(histogram)。笔者写了一个简单的JavaScript框架,用HTML5 Canvas绘画直方图。以下测试代码,可绘画Math.random()的PDF估值(estimate)。


function step() {
    var x = Math.random();
    var bin = Math.floor(x * frequency.length);//譬如,x=0.45,则bin为4,就是给第四个桶的计数加1
    frequency[bin]++;
    sampleCount++;
    plotPdf(frequency, sampleCount, 1/frequency.length, "Estimated pdf of Math.random (n=" + sampleCount + ")");
}
var frequency = new Array(10);
var sampleCount = 0;
for (var i = 0; i < frequency.length; i++)
    frequency[i] = 0;
    
start("canvas1", step);//不断的循环执行step函数



下面是截图

因为有十个桶,所以每个桶的概率大致相同

累积分布函数

密度分布函数,可以变换为累积分布函数(cumulative distribution function, CDF),代表随机变量X小于x的概率:

在X为连续(continuous)的情况下,CDF可用PDF定义:

在X为离散(discrete)的情况下,CDF可定义为:

以下的pdf2cdf()函数,能把离散的PDF数组,转换为CDF数组。由于浮点小数相加会有误差,最后的值可能少于1,有机会产生bug,函数里强制指定最后一个元素为1。

function pdf2cdf(pdf) {//积累函数的意义在于,0表示落在0号桶的计数,1表示落在0,1号桶的计数...所以落在9号桶的概率肯定为1
    var cdf = pdf.slice();
     
    for (var i = 1; i < cdf.length - 1; i++)
        cdf[i] += cdf[i - 1];
 
    // Force set last cdf to 1, preventing floating-point summing error in the loop.
    cdf[cdf.length - 1] = 1;
     
    return cdf;
}

题解

这问题其实正式来说,可称为模拟离散取样(simulated discrete sampling),跟据有限类别的指定概率,来模拟取样。

要制造指定的概率分布随机变量,关键就是如何把均匀分布变换。

逆变换取样

在上节中,显示了CDF的一些特性,例如CDF的范围是,而且是一个单调递增(monotonic increasing)函数。逆变换取样(inverse transform sampling)利用了这些特性,去解决这个问题。逆变换取样方法其实很简单,给一个目标CDF,只要计算其逆函数(inverse function),就可以把均匀的随机变数转换为目标CDF:

这方法能用在所有CDF(包括连续及离散的)。其数学证明可参考维基百科

下图显示这个方法的直观解读,在Y轴范围里均匀取样(),之后向右和CDF取交点,求交点的X轴位置(),X则是符合CDF的概率分布。

以上只是讲的pdf为连续函数的做法,因为y的取值范围是0-1,所以先通过[0,1]均匀分布,获得y的一个取样,然后代入Fx的反函数即可以求出x的取样


这个方法用在离散的情况就更简单,只需搜寻目标的CDF,找出超过均匀取样的元素即可。代码如下:

functiondiscreteSampling(cdf) {//一定要注意cdf是累计概率
    vary = Math.random();
    for(varx incdf)
        if(y < cdf[x])//
            returnx;
             
    return-1; // should never runs here, assuming last element in cdf is 1
}


题目的测试:

var targetPdf = [0.12, 0.4, 0.4, 0.07, 0.01];
var targetCdf = pdf2cdf(targetPdf);
function step() {
    var bin = discreteSampling(targetCdf);
    frequency[bin]++;
    sampleCount++;
    plotPdf(frequency, sampleCount, 0.4, "Estimated cdf of discreteSampling (n=" + sampleCount + ")");
}
var frequency = new Array(targetCdf.length);
var sampleCount = 0;
for (var i = 0; i < frequency.length; i++)
    frequency[i] = 0;
    
start("canvas3", step);

截图是:


分析

在离散的情况下(本文题目要求),其时间复杂度是O(N),其中N为类别数目。

读者可能会注意到,这里用了线性搜寻(linear search),如果targetPdf数组是由大至小排列,平均而言会更快找到结果。另外,也可以用二分搜寻(binary search),那么复杂度会降低为O(lg N),这留给读者作为练习。

事实上,这个问题用二分搜寻是标准的方法。那么,还有没有更快的方法呢?答案是肯定的,例如别名方法(alias method)、近似方法等,有兴趣的读者可参考[1]。当然,在N很小的情况下,线性搜寻和二分搜寻也足够。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值