Я предполагаю, что в вашем примере "вероятность" означает "вес" (таким образом, люди с вероятностью 1,0 не гарантированно выиграют, а общая вероятность не составит 1,0)
Вы можете построить дерево узлов, где конечные узлы содержали ваши отдельные записи:
leaf1 = {:name => "Adam" , :prob => 0.5}
leaf2 = {:name => "Ben" , :prob => 1.0}
и каждый узел содержал сумму узлов под ним
node1 = { :prob_sum => 1.5 , :children=> [ leaf1, leaf2] }
Корневой узел тогда содержит сумму всей структуры
root_node = { :prob_sum => 33 , :children => [ leaf9, leaf10] }
Затем вы выбираете случайное число от нуля до суммы, содержащейся в корневом узле.
my_random = rand( root_node.prob_sum )
Тогда пройдите через дерево. Каждый узел содержит сумму всех узлов под ним, поэтому, если ваше случайное число больше, чем узел, вы вычитаете значение этого узла и пропускаете эту ветвь.
def find_node( my_random ):
c = children.first()
while( c ):
if ( c.prob_sum < my_random ):
return c.find_node(my_random)
my_random -= c.prob_sum
c = c.next
Предполагая, что вы построили сбалансированное дерево, вы должны получить результаты в O (log n)
В качестве альтернативы, вы можете получить тот же результат, добавив поле промежуточного итога к вашему текущему набору данных и выполнив бинарный поиск на основе этого промежуточного итога. Это, вероятно, будет проще реализовать, но будет применимо, только если ваш рабочий набор может поместиться в памяти.