Первое предположение ... выберите случайное число от 1 до 2**size
, найдите базу лога 2 этого и выберите число, которое много элементов в конце.
Прости мой ужасный рубиновый скиллз.
return a[-((Math.log(rand(2**size-1)+1) / Math.log(2)).floor) - 1]
, если rand
возвращает 0, должен быть выбран последний элемент.1 или 2, следующий за последним.3, 4, 5 или 6, третий с конца.И т. Д. Предполагая равномерное распределение случайных чисел, каждый элемент имеет в два раза больше шансов быть выбранным, чем один после него.
Редактировать: На самом деле, похоже, есть функция log2
, поэтому мы ненужно сделать log / log (2).
return a[-(Math.log2(rand(2**size - 1)+1).floor) - 1]
Вы можете вообще избавиться от этих вызовов журнала, как
return a[-((rand(2**size-1)+1).to_s(2).length)]
Но вы создаете дополнительныйString
.Не уверен, что это лучше, чем сложная математика.:)
Редактировать: На самом деле, если вы собираетесь идти по струнному маршруту, вы можете полностью избавиться от +1 и -1.Это сделало бы вероятности более точными, поскольку последние два элемента должны иметь равные шансы быть выбранными.(Если предпоследнее значение не выбрано, последнее значение всегда будет.)
Редактировать: Мы также можем превратить **
в битовый сдвиг, который должен быть немного быстрее (если Руби не был достаточно умен, чтобы сделать это уже).
return a[-(rand(1<<size).to_s(2).length)]