在Ruby中,如何按最小权重进行加权随机选择?

In Ruby, how can one make a weighted random selection by least weight?

如果我有数组:

ar = [1,3,5,3,6,1,4,6,7,6,6,6,6,6]

我可以将其减少到出现次数:

counts = {1=>2, 3=>2, 5=>1, 6=>7, 4=>1, 7=>1}

现在我想选择随机ar中使用最少的数字是更多加权

我了解如何根据最常用的数字而不是它的倒数轻松地进行加权随机选择。

如果您已经有了一个随机加权选择的算法,交换权重的一个选项可以如下所示。

grouping = ar.group_by { |n| n }.transform_values(&:size)
#=> {1=>2, 3=>2, 5=>1, 6=>7, 4=>1, 7=>1}
weights = grouping.values.uniq.sort
#=> [1, 2, 7]
reverse_mapping = weights.zip(weights.reverse).to_h
#=> {1=>7, 2=>2, 7=>1}
grouping.transform_values{ |v| reverse_mapping[v] }
#=> {1=>2, 3=>2, 5=>7, 6=>1, 4=>7, 7=>7}

就是这个主意。


可以重构为更像 Rubyish:

res = ar.group_by { |n| n }.transform_values(&:size).then do |h|
  rev_map = h.values.uniq.sort.then { |w| w.zip(w.reverse).to_h }
  h.transform_values{ |v| rev_map[v] }
end

#=> {1=>2, 3=>2, 5=>7, 6=>1, 4=>7, 7=>7}

这似乎对你有用:

arr = [1,3,5,3,6,1,4,6,7,6,6,6,6,6]

arr.group_by(&:itself).transform_values{|v| arr.size / v.size}.flat_map do |k,v| 
 [k] * v
end.sample

我们将元素分组并计算它们,然后我们创建一个新的 Array,其中元素的数量倒置以支持较少出现的元素。例如

arr.group_by(&:itself).transform_values{|v| arr.size / v.size}.flat_map do |k,v| 
 [k] * v
end.group_by(&:itself).transform_values(&:size)
#=> {1=>7, 3=>7, 5=>14, 6=>2, 4=>14, 7=>14}

原来5出现了一次,现在出现了14次(与4和7一样)。 因此 5,4 和 7 被选中的可能性相同,并且分别是 1 和 3 的两倍,而 1 和 3 的可能性是 6 的两倍和 7 倍。

也许这样的事情可能更有效

grouping =arr.group_by(&:itself).transform_values(&:size).
scale = grouping.values.uniq.reduce(&:lcm)

grouping.flat_map do |k, v|
  [k]  * (scale / v)
end.sample