我试图制作一个随机数字生成器,它会选择更多"均匀地#34; 3到4位数范围之间。如果我这样做:
rand_3_digit_num = (100..999)
rand_4_digit_num = (1000..9999)
rand([rand_3_digit_num, rand_4_digit_num].sample)
我很清楚,在大多数情况下,会选择一个4位数字。我希望给3位数字提供更多被选中的机会,所以我这样做了:
data=
还有其他办法吗?我的目标是让3位数字的数字比普通兰特更大。如果我引入5位数字或6位数字,这个问题会变得更糟,3位数或4位数字被迅速选中的可能性会减少。
答案 0 :(得分:2)
Brute solution:
list = (100..999).to_a*10 + (1000..9999).to_a
=> [100, ..., 9999]
list.size
=> 18000
list.count { |e| e < 1000 }
=> 9000
list.count { |e| 999 < e && e < 10000 }
=> 9000
现在list.sample
应该给出3位和4位数字的相等概率。
答案 1 :(得分:1)
首先指定范围内的概率分布,例如:
range_prob = { (100..999) => 0.2,
(1000..9999) => 0.5,
(10000..43562) => 0.3 }
鉴于这些概率,可以随机选择范围:
def select_random_range(range_prob)
rnd_cum_prob = rand
cum_prob = 0.0
range_prob.each_with_object({}) do |(rng, prob),h|
cum_prob += prob
h[rng] = cum_prob
end.find { |rng, cum_prob| rnd_cum_prob <= cum_prob }.first
end
我在这里做的是从离散概率密度函数(“pdf”)range_prob
构造累积分布函数(“cdf”)。 (参见下图。)为了获得随机变量,我们生成一个介于0和1之间的伪随机数,在垂直轴上绘制,确定水平线与cdf相交的位置,并在水平轴上选择相关值。 / p>
对于上面的range_prob
,
select_random_range(range_prob) #=> 10000..43562
select_random_range(range_prob) #=> 100..999
select_random_range(range_prob) #=> 1000..9999
select_random_range(range_prob) #=> 100..999
select_random_range(range_prob) #=> 10000..43562
在随机范围内选择随机值是一个很小的附加步骤。
rand select_random_range(range_prob) #=> 6467
rand select_random_range(range_prob) #=> 16689
rand select_random_range(range_prob) #=> 2282
rand select_random_range(range_prob) #=> 1317
rand select_random_range(range_prob) #=> 9015
请参阅Kernel#rand。
答案 2 :(得分:0)
我认为你的想法很好。您想要实现的是找到均匀随机N
,其中N
表示数字中的位数,然后找到长度为N
的随机数。
您可以将其拆分为两个函数:
randomSelection(lengths):
K = A random number from the array lengths
return randomNumberForLength(K)
randomNumberForLength(K):
lower_bound = 10^K
upper_bound = 10^(K+1) - 1
return rand(lower_bound, upper_bound)
如果您想在100 - 9999
之间找到一个随机数给两个长度和三个长度的概率相等,您只需拨打randomSelection([2,3])
答案 3 :(得分:0)
这完全取决于你想如何偏向结果。例如,如果你想要一个偶数的机会获得三位或四位数字,你可以使用像(伪代码)这样简单的东西:
def getRand():
if rand(0, 1) == 0: // assume inclusive both ends.
return rand(100, 999)
return rand(1000, 9999)
虽然你两次调用rand
的事实可能会填补真正随机要求的分布,但对于大多数用途来说,这可能已经足够了。
要在单调用中执行此操作,因此可能会保留分布,您只需映射值:
def getRand():
num = rand(1000, 18999)
if num > 9999:
num = (num - 10000) % 900 + 100
这将生成两个大小相等的组1000-9999
和10000-18999
,并将上层组中的值映射为100-999
(因此同样可能会得到三个或四个-digit number):
10000 - 10899 -> 100 - 999
10900 - 11799 -> 100 - 999
11800 - 12699 -> 100 - 999
12700 - 13599 -> 100 - 999
13600 - 14499 -> 100 - 999
14500 - 15399 -> 100 - 999
15400 - 16299 -> 100 - 999
16300 - 17199 -> 100 - 999
17200 - 18099 -> 100 - 999
18100 - 18999 -> 100 - 999
毫无疑问,其他方法可以做到,但这一切都取决于所需的分布。
答案 4 :(得分:0)
对于您描述的问题,您的解决方案已经足够好了。
不过, 999
的出现次数是1000
的10倍。如果要在范围之间进行更平滑的过渡,可以使用:
# Defines a distribution for random numbers between min and max.
# Smaller numbers have a higher probably to appear.
class BiasedGenerator
def initialize(min, max)
@range = (Math.log(min)..Math.log(max))
end
def self.digit_range(min_digit, max_digit)
new(10**(min_digit - 1), 10**max_digit - 1)
end
def rand
Math.exp(Kernel.rand(@range)).round
end
end
您只需要初始化一次:
generator = BiasedGenerator.digit_range(3, 4)
并根据需要多次使用generator.rand
:
random_numbers = (1..1_000_000).map do
generator.rand
end
puts 'Min :'
puts random_numbers.min
puts 'Max :'
puts random_numbers.max
puts
random_numbers.group_by { |n| n.to_s.size }.sort_by(&:first).each do |digits, numbers|
puts "#{digits} digits : #{numbers.size}"
end
输出:
Min :
100
Max :
9999
3 digits : 500061
4 digits : 499939
介于100和999之间的绿色区域应与1000和9999之间的绿色区域几乎相同。
你的发电机也有这个属性:
为了进行比较,这里是Kernel.rand
:
使用BiasedGenerator.digit_range(3, 6)
:
Min :
100
Max :
999998
3 digits : 250342
4 digits : 250714
5 digits : 249814
6 digits : 249130