Ruby 生成具有加权概率的随机数-';分销';宝石

Ruby 生成具有加权概率的随机数-';分销';宝石,ruby,probability,distribution,Ruby,Probability,Distribution,我想创建一个随机数生成器,它生成一个随机十进制数: 大于0.0 小于15.0 其中,该数字接近2.0的概率相对较高 接近15.0或非常接近于零的概率非常低 我的数学非常差,但我的研究似乎告诉我,我想从类似Fisher–Snedecor(F)模式的累积分布函数中提取一个随机数,有点像这样: 我正在使用一个名为Distribution()的RubyGem来尝试实现这一点。它看起来是正确的工具,但我在试图理解如何使用它来实现期望的结果时遇到了困难:(请提供任何帮助。我会收回它,没有rng调用F。

我想创建一个随机数生成器,它生成一个随机十进制数:

  • 大于0.0
  • 小于15.0
  • 其中,该数字接近2.0的概率相对较高
  • 接近15.0或非常接近于零的概率非常低
我的数学非常差,但我的研究似乎告诉我,我想从类似Fisher–Snedecor(F)模式的累积分布函数中提取一个随机数,有点像这样:


我正在使用一个名为Distribution()的RubyGem来尝试实现这一点。它看起来是正确的工具,但我在试图理解如何使用它来实现期望的结果时遇到了困难:(请提供任何帮助。

我会收回它,没有
rng
调用
F
。因此,如果你想使用
分发
gem,我建议使用具有4个自由度的Chi2

具有
k
自由度的Chi2模式等于
k-2
,因此对于4d.f.你将在2处获得模式,请参见。我的红宝石生锈了,请放心

require 'distribution'
normal = Distribution::Normal.rng(0)

g1 = normal.call
g2 = normal.call
g3 = normal.call
g4 = normal.call

chi2 = g1*g1 + g2*g2 + g3*g3 + g4*g4
更新

你必须在15处截断它,所以如果生成的chi2大于15,只需拒绝它并生成另一个 值大于15,请检查PDF/CDF的图形

更新二

如果您想从
F
中获取样本,请为
d
上述代码的自由度制作通用Chi2生成器,并仅检查Chi2的样本比率

更新三


坦率地说,我不知道如何让
F
分布为您工作。在
0
时可以,但模式等于
(d1-2)/d1*d2/(d2+2)
,很难看出它等于2。您提供的图的模式约为1/3。

这里有一个非常粗糙、不科学、非数学的尝试,试图使用F分布和您在F函数图(3和36)中给出的参数

首先,我计算CDF为0.975所需的F值(数字15的上限为100%-2.5%):

为了计算,我们可以使用
p_值
方法,如下所示:

> F_15 = Distribution::F.p_value(0.975, 3, 36)
=> 3.5046846420861977
接下来,我们只需使用一个乘数,这样当我们计算CDF时,当F值为
F_15
时,它将返回值15

> M = 15 / F_15
=> 4.27998565687528
现在我们可以用
rand
生成随机数,其范围为0..1,如下所示:

[M * Distribution::F.p_value(rand, 3, 36), 15].min
问题是这个函数会以45%的概率接近数字2吗?嗯……有点。你需要为F分布选择正确的参数来调整曲线(或者只是调整乘数
M
)。但下面是一个示例,其中包含了来自你图像的参数:

0.step(0.99, 0.02).map { |n| 
  sprintf("%0.2f", M * Distribution::F.p_value(n, 3, 36)) 
}
给你:

["0.00", "0.26", "0.42", "0.57", "0.70", "0.83", "0.95", "1.07", 
 "1.20", "1.31", "1.43", "1.55", "1.67", "1.80", "1.92", "2.04", 
 "2.17", "2.30", "2.43", "2.56", "2.70", "2.84", "2.98", "3.13", 
 "3.28", "3.44", "3.60", "3.77", "3.95", "4.13", "4.32", "4.52", 
 "4.73", "4.95", "5.18", "5.43", "5.69", "5.97", "6.28", "6.61", 
 "6.97", "7.37", "7.81", "8.32", "8.90", "9.60", "10.45", "11.56",
 "13.14", "15.90"]

有时,由于数据的性质,您知道哪个分布适用。例如,如果随机变量是独立的、相同的伯努利(双态)之和随机变量,你知道前者有一个二项式分布,可以近似为正态分布。当这里不适用时,你可以使用一个连续分布,由它的参数决定,或者简单地使用一个离散分布。其他人提出了使用各种连续分布的建议,所以我将继续关于使用离散分布的一些注释

假设离散概率密度函数如下:

pdf = [[0.5, 0.03], [1.0, 0.06], [1.5, 0.10], [ 2.0, 0.15], [2.5 , 0.15], [ 3.0, 0.10],
       [4.0, 0.11], [6.0, 0.14], [9.0, 0.10], [12.0, 0.03], [14.0, 0.02], [15.0, 0.01]] 


pdf.map(&:last).reduce(:+)
  #=> 1.0
这可以解释为随机变量小于0.5的概率为0.03,随机变量大于或等于0.5且小于1.0的概率为0.06,依此类推

离散pdf可以从历史数据或通过抽样来构建,这是它比使用连续分布的优势。它可以通过增加区间数来任意细化

接下来,将pdf转换为累积分布函数:

cum = 0.0
cdf = pdf.map { |k,v| [k, cum += v] }
  #=> [[0.5, 0.03], [1.0, 0.09], [1.5, 0.19], [2.0, 0.34], [2.5, 0.49], [3.0, 0.59],
  #    [4.0, 0.7], [6.0, 0.84], [9.0, 0.94], [12.0, 0.97], [14.0, 0.99], [15.0, 1.0]] 
现在,用于生成
0.0
1.0
之间的伪随机变量,并用于将随机变量与
cdf
键关联:

def rnd(cdf)
  r = rand
  cdf.find { |k,v| r < v }.first
end

您需要比接近2.0和“非常低”的“相对较高”概率更具体接近15.0的概率…让我们假设“相对较高”表示约45%,而“非常低”表示目前约2.5%。一旦我理解了函数,我可以调整这些变量以获得预期效果。你想确定适当的统计分布是对的。你的问题是我们没有人在许多可能的分布(β-截断F、卡方、γ-三角形、分段均匀等)之间进行选择有任何依据您是否有任何真实的数据可以帮助您确定什么是合适的分布?如果有,您的第一步应该是使用一个可以进行分布拟合的统计数据包来确定哪些分布将是合适的。@pjs I wrt
F
采样错误,没有
rng
方法,请检查更新您需要一个截断循环以将其限制在15以内。另外请注意,您定义了
normal
,然后使用
normall
调用它(两个l)。感谢大家对我的问题所作的贡献!提出的所有解决方案似乎都很好,我知道会帮助其他人。我发现基本Chi2解决方案适合我的特定问题,但我期待着尝试其他解决方案。wrt
为F分布选择正确的参数,以调整曲线像没有数量的调整将产生模式在2。检查我的更新III@SeverinPappadeux是的,您必须使用
max
进行截断。当然,对于“接近2”,您无法获得45%的准确概率因为他在他的例子中预先选择了F分布的参数。正如我所说的。这是一个非数学解。只是一个例子。我们需要精确的模型
def rnd(cdf)
  r = rand
  cdf.find { |k,v| r < v }.first
end
n = 100_000
inc = 1.0/n

n.times.with_object(Hash.new(0.0)) { |_, h| h[rnd(cdf)] += inc }.
  sort.
  map { |k,v| [k, v.round(5)] }.to_h
  #=> { 0.5=>0.03053, 1.0=>0.05992, 1.5=>0.10084, 2.0=>0.14959, 2.5=>0.15024,
  #     3.0=>0.10085, 4.0=>0.10946, 6.0=>0.13923, 9.0=>0.09919, 12.0=>0.03073, 
  #    14.0=>0.01931, 15.0=>0.01011}