我有一个包含 3 个元素的数组,我想根据可能性选择一个并将其添加到另一个数组中。
例如,num 1 有 5% 的机会被选中,num 2 有 60% 的机会被选中,num 3 有 35% 的机会被选中。
arr = [{:num=>1, :diff=>-29}, {:num=>2, :diff=>5}, {:num=>3, :diff=>25}]
我从 stackoverflow 中找到了以下方法,只是想知道这是否可行?或者还有其他方法吗?
def get_num(arr)
case rand(100) + 1
when 1..5
p arr[0]
when 6..65
p arr[1]
when 66..100
p arr[2]
end
end
get_num(arr)
谢谢!
最佳答案
您的代码没问题,但还有另外两种方法。
使用累积分布函数(“CDF”)
CDF = [[0.05,0], [0.05+0.60,1], [0.5+0.60+0.35,2]]
#=> [[0.05,0], [0.65,1], [1.0,2]]
def get_num(arr)
n = rand
arr[CDF.find { |mx,_idx| n <= mx }.last]
end
arr = [{:num=>1, :diff=>-29}, {:num=>2, :diff=>5}, {:num=>3, :diff=>25}]
get_num(arr)
#=> {:num=>2, :diff=>5}
get_num(arr)
#=> {:num=>2, :diff=>5}
get_num(arr)
#=> {:num=>3, :diff=>25}
get_num(arr)
#=> {:num=>1, :diff=>-29}
get_num(arr)
#=> {:num=>2, :diff=>5}
假设:
n = rand
#=> 0.5385005480168696
然后
a = CDF.find { |mx,_idx| n <= mx }
#=> [0.65,1]
i = a.last
#=> 1
arr[i]
#=> {:num=>2, :diff=>5}
请注意,我遵循以下惯例:find
的第二个 block 变量 (_idx
) 的名称以下划线开头,以向读者表明该 block block 计算中不使用变量。通常只使用下划线
(_
)。
现在考虑如果进行了 n
抽取,arr
的每个元素将被随机抽取的次数的分数:
def outcome_fractions(arr, n)
n.times
.with_object(Hash.new(0)) { |_,h| h[get_num(arr)] += 1 }
.transform_values { |v| v.fdiv(n) }
end
从索引数组中随机选择
outcome_fractions(arr, 1_000)
#=> {{:num=>2, :diff=>5} =>0.612,
# {:num=>3, :diff=>25} =>0.328,
# {:num=>1, :diff=>-29}=>0.06}
outcome_fractions(arr, 100_000)
#=> {{:num=>3, :diff=>25} =>0.34818,
# {:num=>1, :diff=>-29}=>0.04958,
# {:num=>2, :diff=>5} =>0.60224}
请注意,随着样本量的增加,随机抽取的每个散列的分数接近其指定的总体概率(尽管“伪随机”抽取并不是真正随机的)。
不要关心 outcome_fractions
的工作原理。
这是另一种更高效的方法(因为它不使用执行线性搜索的find
)但使用更多内存。
CHOICE = [*[0]*5, *[1]*60, *[2]*35]
#=> [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
# 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
# 2, 2, 2, 2, 2]
def get_num(arr)
arr[CHOICE[rand(100)]]
end
#=> {{:num=>2, :diff=>5} =>0.60029,
# {:num=>3, :diff=>25}=>0.35022,
# {:num=>1, :diff=>-29}=>0.04949}
注意:
[*[0]*5, *[1]*60, *[2]*35]
产生与
相同的数组[[0]*5, [1]*60, [2]*35].flatten
*[0]*5
中的第一个*
是splat operator ;第二个是方法 Array#* . [0]*5 #=> [0,0,0,0,0]
首先求值。
CHOICE
有 100 个元素。例如,如果这三个概率是 0.048
、0.604
和 0.348
,CHOICE
将有 10* *3 #=> 1_000
个元素(48
个零,604
个和 348
个)。
https://stackoverflow.com/questions/69339733/