Вам уже показалось несколько совершенно хороших ответов, которые зависят от знания длины списка заранее.
Чтобы честно выбрать один элемент из списка без , для начала нужно знать длину списка:
if (list.empty()) error_out_somehow
r=list.first() // r is a reference or pointer
s=list.first() // so is s
i = 2
while (r.next() is not NULL)
r=r.next()
if (random(i)==0) s=r // random() returns a uniformly
// drawn integer between 0 and i
i++
return s
(полезно, если ваш список хранится как связанный список)
Чтобы распределить призы в этом сценарии, просто пройдите по списку призов, выбирая случайного победителя для каждого. (Если вы хотите предотвратить двойной выигрыш, удалите победителя из списка участников.)
Почему это работает?
- Вы начинаете с первого элемента в
1/1
- На следующем проходе вы выбираете второй элемент наполовину (
1/2
), что означает, что первый элемент имеет вероятность 1 * (2-1)/2 = 1/2
- на дальнейших итерациях вы выбираете n-й предмет с вероятностью
1/n
, и вероятность каждого предыдущего предмета уменьшается в (n-1)/n
, что означает, что когда вы подходите к концу, вероятность наличия m
-го элемента в списке (из n
элементов) составляет
1/m * m/(m+1) * (m+1)/(m+2) * ... * (n-2)/(n-1) * (n-1)/n = 1/n
и одинаково для каждого предмета.
Если вы обратите внимание, вы заметите, что это означает обход всего списка каждый раз, когда вы хотите выбрать элемент из списка, так что это не максимально эффективно для (скажем) переупорядочения всего списка (хотя это и делает это справедливо).