Предположим,
vowels = { A: 5, E: 8, I: 4, O: 7, U: 3, Y: 1 }
Я понимаю, что значения этого хэша являются весами отрисовки связанных ключей.Сначала вы строите (по-другому):
v_choice = vowels.flat_map { |k,v| [k]*v }
#=> [:A, :A, :A, :A, :A,
# :E, :E, :E, :E, :E, :E, :E, :E,
# :I, :I, :I, :I,
# :O, :O, :O, :O, :O, :O, :O,
# :U, :U, :U,
# :Y]
Затем вы выполняете
x.times {print v_choice.shuffle!.pop}
Предположим, что первый случай перемешивания производит следующее:
v_choice
#=> [:O, :O, :E, :U, :U, :O, :A, :E, :E, :E, :U, :I, :A, :I,
# :E, :A, :O, :O, :E, :E, :O, :A, :I, :A, :O, :E, :I, :Y]
, а затем
print v_choice.pop
печатает "Y"
и удаляет только :Y
из v_choice
.Следовательно, (с вероятностью 1) :Y
будет отображаться случайным образом не более, что противоречит тому факту, что оно имеет положительный вес.
Если значения vowels
являются не весами, а числами в конечном набореиз гласных можно сильно упростить.После вычисления v_choice
нужно только написать
puts v_choice.sample(x).join
# EOOYEIIEOI
или
puts v_choice.shuffle[0,x].join
# EOOUOEEIAE
Теперь давайте предположим, что значения vowels
действительно являются весами.Тогда нам нужно только написать
x = 10
x.times { print v_choice.sample }
# OAEYEUAOAO
Если значения vowels
велики, v_choice
соответственно велика.Это повлияет на требования к памяти, но не на скорость выполнения.Если бы требования к памяти были дорогими (вряд ли, возможно), можно было бы сделать следующее.
tot_wts = vowels.values.sum.to_f
#=> 28.0
cum = 0
cum_dist = vowels.map do |v,wt|
cum_wt = cum + wt/tot_wts
cum = cum_wt
[v, cum_wt]
end
#=> [[:A, 0.17857142857142858],
# [:E, 0.4642857142857143],
# [:I, 0.6071428571428572],
# [:O, 0.8571428571428572],
# [:U, 0.9642857142857143],
# [:Y, 1.0]]
В случае ошибки округления мы могли бы установить
cum_dist[-1][-1] = 1.0
Затем мы можем произвеститаким образом, взвешенная выборка.
x = 10
x.times do
rn = rand
print cum_dist.find { |_,cum| rn < cum }.first
end
# EAAOOEEIOA
Обратите внимание, что мы не можем записать
print cum_dist.find { |_,cum| rand < cum }.first
, потому что значение, возвращаемое rand
, будет постоянно меняться в блоке find
.
Этот метод, так называемый «метод обратного преобразования», обычно используется для генерации дискретных случайных величин в имитационных моделях.