Ми}{@лbI4

Блог хеллоуворлдщика

Рандомизация. Генерация случайных чисел.

14.12.2014 рандомизация, советы, советы

Нижеописанное относится к PHP и все примеры приводятся для этого языка.

На пути моём богатырском встала задача реализация рандомизации. Ранее опыта не имел, но знал, что встроенная рандомизация шлак. По сути, да, она работает, но не так, чтобы очень. Хотя, притензий, как программист, я к ней не имею. Но, как юзверь - да. Самая главная проблема, это отсутствие "логики". И как бы, херали она там вообще должна быть? Именно её отсутствие и создаёт некоторые проблемы на практике.

Допустим, у нас есть числа "1, 34, 90, 87, 65, 45, 103, 0, 12, 500, 44, 66". Стоит задача вывести 5 случайных числа. Хм...Вроде всё просто. Пока мы не использовали встроенную функцию rand():

$a = [1, 34, 90, 87, 65, 45, 103, 0, 12, 500, 44, 66];
for ($i = 0; $i != 5; $i++)
{
	$result[] = $a[rand(1, count($a)) - 1];
}
echo implode(', ', $result);

Этот код после выполнения выдал такой результат:

0, 0, 90, 44, 44

Что вовсе нас не устраивает. Мы же не хотим, чтобы были дубли. Тогда мы можем добавить проверку на дубли, либо использовать другую функцию, которая перемешает массив и после мы можем вытащить 5 первых результатов.

$a = [1, 34, 90, 87, 65, 45, 103, 0, 12, 500, 44, 66];
shuffle($a);
for ($i = 0; $i != 5; $i++)
{
	$result[] = $a[$i];
}
echo implode(', ', $result);

Здесь мы использовали встроенную функцию перемешивания массива shuffle(). Она так-же прекрасно работает, как и rand().

Результат:

103, 12, 65, 0, 44

Уже лучше! Но не так, чтобы очень. Давайте рассмотрим реальную задачу и реальные проблемы.

А теперь по-пацански

Перед нами задача "Реализовать случайный розыгрыш приза среди участников конкурса.". Все сразу вспомнили про rand() или, если участники есть в базе (а это по-любому), то про ORDER BY RAND() LIMIT 1. Хочу вас огорчить, но не одно из решений не является хорошим, т.к. дубли обеспечены. И как тогда быть? Всё просто - нужно добавить немного логики, или, проще говоря, подтасовать результаты.

Рандомизация в PHP очень честная и справедливая, как в прочем-то и везде. Есть такая вероятность, что может выпасть как минимум 3 раза подряд одинаковый результат, особенно, если победитель может быть только один, как в нашем случае. Но, вы можете сказать "а почему бы не использовать srand()?", но это так-же не спасает нас от дублей, т.к. использование данной функции происходит автоматически с PHP 4.2.0. Хотя, вероятность их выпадения может значительно уменьшиться, если рандом "раскачать" используя эту функцию примерно так:

srand((double)microtime() * time());

Но, вероятность выпадения одинаковых значений есть всегда, увы.

Давайте посмотрим, как ведём мы себя, люди, при выборе какого-то случайного победителя, если этот победитель уже выигрывал у нас неоднократно, и среди участников метеж и бунт, т.к. они считают что победитель подставной?

  1. Мы возьмём выкинем этого победителя из списка и выберем другого. А так-же, чтоб у нас все остались довольны, мы будем вести у себя учёт победителей и неявно введём такое понятие как "коэффициент удачи", а именно новички и те, кто уже больше всех проиграл - вне очереди. Тем самым, мы оставим всех довольными.

  2. После генерации случайного победителя, мы можем пробежаться по всему списку победителей и вычислить процент побед каждого из победителя. И если этот победитель уж очень много раз побеждал - генерируем другого, пока не выберем кого-то извне списка победителей, либо того, кто еще не выигрывал.

Я указал два варианта, которые, по моему мнению, достойны права на жизнь. На практике же помимо побед, учитывается еще много чего, всё зависит от заказчика или от результата. На худой конец можно просто "раскачать" рандом, да и чаще всего этого более, чем достаточно.

Итог

Рандомизация довольно сырая и актуальная тема. Если у тебя есть что-то интересное - обязательно поделись в комментариях, я буду рад! =)