Tau*_*ren 3 javascript random math node.js
我正在尝试使用以下签名创建一个函数:
function weightedRandom (target, probability) {
// calculate weighted random number that is more likely to be near target value
return value; // number from 0 to 1
}
Run Code Online (Sandbox Code Playgroud)
它应该做到以下几点:
例如,weightedRandom(0.8, 0.2)会导致20%的随机值可能聚集在值0.8附近,但可能是从0到1的任何数字.如果概率为0.5,那么返回的结果随机值将会更多接近0.8.我想可能还需要另一个参数来定义簇的宽度(标准偏差?).
我不是数学家,但我被告知要将Beta发行版作为一种可能的工具来帮助:

我发现了一些具有beta功能的NPM模块,但我不知道如何使用它们来解决这个问题:
TLDR:在两个更简单的分布之间随机选择,也是逆变换采样
如果您的分布平坦,您可以平均选择范围内的任何值.如果你有高斯分布,你可以选择接近高斯平均值的值.所以考虑随机选择做其中一个或另一个.
如果您希望随机值在t80%的时间内接近目标,而在其他地方则为其他20%.假设'near'意味着在2个标准差内,我们将采用方差v.这样的范围内(t-2*v),以(t+2*v)需要以覆盖P(0.8).
假设我们将随机使用平面分布或高斯分布; 然后,随机值落在给定范围内的概率是两个分布的总和,由分布选择的偏差加权.如果我们选择高斯,我们将得到一个2 std.dev内的值.95.45%的时间.如果我们采用高斯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).
Run Code Online (Sandbox Code Playgroud)
我们有2个未知数,所以如果我们还需要一个非常接近概率的值,该值在60%的时间内在目标的1 std.dev之内,那么
0.6 = 0.6827*X + (1-X)(2v/r).
Run Code Online (Sandbox Code Playgroud)
重新排列(2v/r):
(0.8 - 0.9545*X)/(1-X)*2 = (2v/r)
(0.6 - 0.6826*x)/(1-X) = (2v/r)
Run Code Online (Sandbox Code Playgroud)
等同和简化
X = 0.81546
Run Code Online (Sandbox Code Playgroud)
从而:
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]));
}
Run Code Online (Sandbox Code Playgroud)
我认为这可以为您提供所需的形状,让您为近概率和非常接近的概率选择两个概率,但避免过多的复杂性.
当我被要求提供更多实现时,我发现了一个正常的变量生成器(感谢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;
};
Run Code Online (Sandbox Code Playgroud)
我考虑的第一种方法是使用逆变换采样,我将在此尝试解释.
假设我们有一个分布,0到4的值同样可能,但只有4到10的值的一半.总概率是4a + 6(2*a)= 1,所以a = 1/16:

假设你有一个函数,当给定0到1之间的值时,产生一个0到10之间的值; 它仍然是单调的(没有最小值/最大值),但是如果你从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());
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2813 次 |
| 最近记录: |