function weightedRandom (target,probability) { // calculate weighted random number that is more likely to be near target value return value; // number from 0 to 1 }
它应该做到以下几点:
>生成从0到1的随机数,但不包括1
>在该范围内挑选任何给定数字的概率不均匀分布
>挑选的数字接近目标值的可能性更大(目标也是0到1之间的值)
>概率曲线看起来像钟形曲线,其中目标值具有最高概率,并且其周围的值逐渐减小,但0到1范围内的所有值仍有机会被挑选.
>可以使用概率值调整此机会的权重,其中值0表示不对随机性应用加权,1表示几乎所有拾取的数字将聚集在目标值周围.
例如,weightedRandom(0.8,0.2)会产生一个随机值,该值可能会聚集在0.8左右,但可以是从0到1的任意数字.如果概率为0.5,那么甚至更多返回的结果随机值将接近0.8.我想可能还需要另一个参数来定义簇的宽度(标准偏差?).
我不是数学家,但我被告知将Beta Distributions作为一种可能的工具来帮助:
我发现了一些具有beta功能的NPM模块,但我不知道如何使用它们来解决这个问题:
> https://github.com/AndreasMadsen/mathfn
> https://github.com/jstat/jstat
解决方法
合并两个发行版
如果您的分布平坦,您可以平均选择范围内的任何值.如果你有高斯分布,你可以选择接近高斯平均值的值.所以考虑随机选择做其中一个或另一个.
如果您希望随机值在80%的时间内接近目标t,则在其他地方接近20%.假设’near’意味着在2个标准差内,我们将方差视为v.因此范围(t-2 * v)到(t 2 * v)需要覆盖P(0.8).
假设我们将随机使用平面分布或高斯分布;然后,随机值落在给定范围内的概率是两个分布的总和,由分布选择的偏差加权.如果我们选择高斯,我们将得到值within 2 std.dev. 95.45% of the time.如果我们采用高斯X%的时间,那么近概率Pn = P(t-2v到t 2v)= 0.9545 * X(1-X) (4v / r),其中r是全范围,(4v / r)则是范围内平坦分布的比例.
要使此Pn达到80%:
0.8 = 0.9545*X + (1-X)(4v/r).
我们有2个未知数,所以如果我们还需要一个非常接近概率的值,该值在60%的时间内在目标的1 std.dev之内,那么
0.6 = 0.6827*X + (1-X)(2v/r).
重新排列(2v / r):
(0.8 - 0.9545*X)/(1-X)*2 = (2v/r) (0.6 - 0.6826*x)/(1-X) = (2v/r)
等同和简化
X = 0.81546
从而:
var range = [0,10]; var target = 7.0; var stddev = 1.0; var takeGauss = (Math.random() < 0.81546); if(takeGauss) { // perform gaussian sampling (normRand has mean 0),resample if outside range while(1) { var sample = ((normRand()*stddev) + target); if(sample >= range[0] && sample <= range[1]) { return sample; } } } else { // perform flat sampling return range[0]+(Math.random()*(range[1]-range[0])); }
我认为这可以为您提供所需的形状,让您为近概率和非常接近的概率选择两个概率,但避免过多的复杂性.
当我被要求提供更多实现时,我发现了a normal variate generator (thanks Prof Ian Neath):
function normRand() { var x1,x2,rad; do { x1 = 2 * Math.random() - 1; x2 = 2 * Math.random() - 1; rad = x1 * x1 + x2 * x2; } while(rad >= 1 || rad == 0); var c = Math.sqrt(-2 * Math.log(rad) / rad); return x1 * c; };
逆变换采样
我考虑的第一种方法是使用Inverse transform sampling,我将在这里解释.
假设我们有一个分布,0到4的值可能相同,但只有4到10的值的一半.总概率是4a 6(2 * a)= 1,所以a = 1/16:
假设你有一个函数,当给定0到1之间的值时,产生一个0到10之间的值;它仍然是monotonic(没有最小值/最大值),但如果你从0到1每0.01递增一次,你会得到4:6 * 2 = 1:3的比例,所以4倍于4的值.该函数看起来像这样:
我们具有从z = 0到z = 1/3的线性段,其中x(1/3)= 4,然后从z = 1/3到z = 1的线性段继续到x(1)= 10.如果我们从0和1之间的平坦概率分布中选择一个随机数z,那么x(z)将按照范围的前1/3分配,根据需要给出最多4的值,以及上面的余数.
然后,z(x)是逆变换,它采用平坦分布并从期望分布产生屈服值.如果你想绘制它,它是x <(1/3)? 9 * x:12 * x -1. 然后游戏构建一个你满意的分布,并通过使用上面的碎片或通过分析反演它或一些近似(高斯逆不能分析写下)来反转它以获得逆变换.通过这种方式,您可以将任何平面分布的样本转换为所需的分布. 从上面的步骤分布中取样将如下所示:
// transform 0-1 flat to 0-10 stepped function stepInvTransform(z) { return (3*z < 1 ? 9*z : (12*z - 1)); } // sample via inv transform var sample = stepInvTransform(Math.random());