我想创建一个随机数生成器,它生成一个随机的十进制数:
我在数学上非常匮乏,但我的研究似乎告诉我,我想从类似Fisher-Snedecor(F)模式的累积分布函数中拉出一个随机数,有点像这样:
我正在使用名为Distribution(https://github.com/sciruby/distribution)的Ruby gem来尝试实现这一目标。它看起来像是正确的工具,但我正在努力了解如何使用它来达到预期的结果:(请帮助。
答案 0 :(得分:2)
我会收回它,rng
没有F
号召唤。所以,如果你想使用Distribution
gem,我建议使用4个自由度的Chi 2 。
k
自由度的Chi 2 的模式等于k-2
,因此对于4 d.f.您将获得2的模式,请参阅here。我的Ruby生锈了,忍受我
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的图表。
更新II
如果您想从F
获取样本,请从上面的代码中为d
自由度制作通用Chi 2 生成器,并且只采样chi2的比例,检查here
chi2_d1 = DChi2(d1)
chi2_d2 = DChi2(d2)
f = (chi2_d1.call / d1) / (chi2_d2.call / d2)
更新III
而且,坦率地说,我不知道如何让F
发行版为你工作。可以0
,但模式等于(d1-2)/d1 * d2/(d2 + 2)
,很难看出它等于2.你提供的图表的模式大约为1/3。
答案 1 :(得分:2)
这是一个非常粗糙,不科学,非拙劣的尝试,使用你在F函数图像(3和36)中给出的参数进行F分布。
首先,我计算出CDF需要的F值为0.975(对于15号的范围的上限,100% - 2.5%):
要计算我们可以使用p_value
方法,如下所示:
> 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
问题是这个函数是否接近2号,概率为45%?嗯..很多。您需要为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"]
答案 2 :(得分:1)
有时您会因为数据的性质而知道哪个分布适用。例如,如果随机变量是独立的,相同的伯努利(两态)随机变量的总和,则您知道前者具有二项分布,其可以通过正态分布近似。当这里不适用时,您可以使用由其参数形成的连续分布,或者仅使用离散分布。其他人已经提出了使用各种连续分布的建议,因此我将传递关于使用离散分布的一些评论。
假设离散概率密度函数如下:
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,等等。
离散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]]
现在使用Kernel#rand在0.0
和1.0
之间生成伪随机变量,并使用Enumerable#find将随机变量与cdf
密钥相关联:
def rnd(cdf)
r = rand
cdf.find { |k,v| r < v }.first
end
请注意,cdf.find { |k,v| rand < v }.first
会产生错误的结果,因为rand
的每个键值对都会执行cdf
。
让我们尝试100,000次,记录相对频率
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}