@l4m3r

Как сделать рандом из массива с указанной вероятностью для элементов?

Есть массив вида:
$a = [
     'a' => 1,
     'b' => 0.11,
     'c' => 0.5,
     'd' => 1,
     'e' => 0.02,
     'f' => 0.0000005,
// ...
];


Значения в массиве != вероятность (0 - 1), а лишь вес / частота.
Мне нужно сделать такой рандом, который чаще всего будет возращать тот ключ, у которого больший вес.
Т.е. согласно примеру выше, чаше всего будет выпадать 'a' или 'd', крайне редко 'e'

PS: была подобная тема, но там немного не то.
  • Вопрос задан
  • 230 просмотров
Решения вопроса 3
sergiks
@sergiks Куратор тега PHP
♬♬
Сложить все веса. Их сумма это 100%, «единица».
Взять случайное число от 0 до 1.
Спроецировать на ваш диапазон, посмотреть, куда попали.

$sum = array_sum($a);
$rnd = rand() / getrandmax(); // от 0 до 1
$runningSum = 0;
foreach($a as $k => $v) {
  $runningSum += $v / $sum;
  if ($runningSum >= $rnd) {
    $key = $k;
    break;
  }
}
if (!$key) $key = $k;

echo "Выпало: " . $key . PHP_EOL;
Ответ написан
А я бы все веса поделил на наименьшее из них (N_i = вес_i / вес_min), и с полученными значениями N_i создал бы массив из букв, где каждая повторяется N_i количество раз.

А далее или перемешал бы его, или брал бы из него случайным образом очередную букву
Ответ написан
iCoderXXI
@iCoderXXI
React.JS/FrontEnd engineer
Приводим коэффициенты к целым числам пропорционально так, чтобы минимальный коэффициент равнялся единице, остальные округляем до единиц. Таким образом получаем на выходе массив с целыми числами, где отношения в пропорции элементов друг к другу будут близкими к изначальным. Далее на основе этого промежуточного массива генерируем новый, с диапазонами, для первого элемента от 0 до его значения, для каждого последующего от суммы всех предыдущих значений до сумма + текущее значение. Таким образом весь массив диапазонами покрывает значения от 0 до суммы всех величин из первого промежуточного массива, которую обозначим как S. Далее используем только второй массив с диапазонами, для каждого элемента выборки генерим рандомное число R от 0 до S, и находим ключ согласно тому диапазону, куда в каждой итерации попадает R.

По идее данный алгоритм идентичен варианту Дмитрия, но эффективен по части использования памяти. Опять же, его тоже можно оптимизировать. :)

ЗЫ: Те же яйца, но в профиль предложил Сергей Соколов
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы