#php #массивы #случайные_числа
Есть такой набор данных:
$items = array(
array(
'name' => 'Элемент #1',
'chance' => 1,
),
array(
'name' => 'Элемент #2',
'chance' => 5,
),
array(
'name' => 'Элемент #3',
'chance' => 10,
),
);
Для каждого элемента массива предопределена вероятность — в ключе 'chance'.
Задача: выбрать случайный элемент из этого набора, учитывая, что вероятность того
что выпадет «Элемент #2» в 5 раз выше, чем «Элемент #1», а «Элемент #3» — в 10 раз
выше, чем у «Элемент #1».
Если точнее, то, наверно, так:
Элемент #1 — вероятность: 1/16
Элемент #2 — вероятность: 5/16
Элемент #3 — вероятность: 10/16
Набросал такой алгоритм:
$items = [...]; // входной массив (см. выше)
$items_set = array();
foreach ($items as $item_id => $item)
{
for ($i = 0; $i < $item['chance']; $i++)
$items_set[] = $item_id;
}
// теперь $items_set представляет собой такой массив:
// [0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
// то бишь, индекс элемента входного массива повторяется столько,
// сколько у него «шансов» быть избранным
$rand_id = $items_set[array_rand($items_set)];
$rand_item = $items[$rand_id];
На выходе $rand_item хранит как-раз то, что необходимо — один из элементов входного
массива. И все бы хорошо, если бы при добавлении элемента с вероятностью 200, массив
$items_set не рос на 200 элементов. И таких может быть несколько.
Вопрос: есть ли какой-то более адекватный способ решения задачи?
Если алгоритм будет уметь оперировать с элементами с вероятностью = 0.5 — будет вообще
здорово (мой вариант к этому не приспособлен, приходится предварительно подбирать множитель
для всех «шансов» так, чтобы минимальный был = 1).
Ответы
Ответ 1
да, такой алгоритм есть. Он заключается в том, что нужно сформировать массив вида [1,6,16]. ( То есть, если есть массив вероятностей [1,5,10], то нужный массив это такой, что элемент в позиции n - это сумма элементов первого массива от 1 до n). Последний элемент этого массива - это суммарное кол-во вариантов. Как использвоать: генерируем случайное число и проходим по массиву, пока не найдем элемент, который больше сгенерированного числа (я предполагаю, что случайные числа генерятся в диапазоне от 0 до сумма минус 1). Но если элементов больше 7-10, то лучше уже подумать о бинарном поиске, благое дело, он будет быстрым (массив то гарантированно отсортирован).Ответ 2
Попробуйте хранить интервалы вероятностей для каждого элемента: Элемент №1 - от 0 (включительно) до 1 (не вкл.) Элемент №2 - от 1 (вкл.) до 6 (не вкл.) Элемент №3 - от 6 (вкл.) до 16 (не вкл.) Теперь ваша задача заключается в том, чтобы взять случайное число от 0 до 15, проверить, в какой интервал оно попадает и взять соответствующий элемент.Ответ 3
Решил похожую задачу просто - указал диапазон чисел для слов. Чем больше диапазон чисел для слова, тем больше вероятность его вывода. $bb = rand(0,1000); if ($bb >= 0 && $bb <= 700) { echo "значение 1 ".$bb; } elseif ($bb >= 701 && $bb <= 1000) { echo "значение 2 ".$bb; }
Комментариев нет:
Отправить комментарий