Мы не будем касаться алгоритмов генерации случайных чисел — вряд ли они будут сильно интересовать веб-мастера, которому нужно просто вывести на странице случайную картинку. Единственное, о чем необходимо упомянуть — это необходимость «запустить» генератор случайных чисел перед использованием — если этого не сделать, то выдаваемые им значения будут повторяться при каждом прогоне скрипта.
В большинстве языков для этого используется функция seed или srand, которой передается какое-то случайное число — как правило, текущее время. Стоит отметить, что в современных версиях PHP (выше, чем 4.2) делать это не обязательно — генератор случайных чисел инициализируется автоматически.
В PHP случайное число можно получить с помощью функции rand(). Эта функция может принимать два необязательных параметра — минимальное и максимальное значение. Для того чтобы узнать самое большое случайное число, которое можно получить с помощью этой функции, можно воспользоваться функцией getrandmax(). Впрочем, вряд ли это потребуется на практике.
Извлечение случайного элемента массива требуется довольно частоВторой полезной в деле «ослучайвливания» сайта функцией является array_rand(). Она занимается тем, что возвращает индекс (или массив индексов) одного или нескольких случайных элементов заданного массива. Использовать ее удобно, например, для случайного выбора фонового цвета какого-то элемента. Так как автоматическое генерирование цветов — занятие неблагодарное (слишком легко получить неподходящие для сайта варианты), то значительно проще создать массив с заранее подобранными сочетающимися цветами, а потом выбирать из него случайные элементы. Например
$colors = array('#93FAE4', '#96E3FF', '#96C1FF', '#9990F5');
$color = $colors[array_rand($colors)];
echo "<table bgcolor=$color>"
...
Помимо выбора цвета, очень востребованным является случайный выбор строки из файла. Например, вы можете создать текстовый файл с различными цитатами и выводить случайную цитату при просмотре страницы. Или, что более актуально, хранить в файле рекламную информацию (в частности, коды баннеров) и показывать ее. Так как количество строк в файле заранее неизвестно (и, в принципе, может изменяться, особенно если файл создается тоже автоматически), то нам придется файл прочитать, а потом уже выбрать какую-то строку. Самым очевидным и простым решением будет использование функции file(), которая читает файл и возвращает его в виде массива, где каждый элемент представляет собой строку исходного файла. Ну, а получив массив, можно воспользоваться уже известной нам функцией array_rand().
Читать файл целиком — очень неэкономно!Однако недостатки такого метода очень быстро проявятся, если файл достаточно велик, а посетителей много. Ведь полученный массив занимает память, а ее всегда мало... Поэтому можно поступить следующим образом: читать файл построчно и с вероятностью обратно пропорциональной числу прочитанных строк сохранять текущую строку в качестве случайной.
$f = fopen('jokes.txt', 'r');
$i = 0;
while(!feof($f)) {
$tmp = fgets($f, 8196);
if (!rand(0, $i++)) $string = $tmp;
}
fclose($f);
$string = trim($string);
Давайте разберемся, как этот пример работает. Первым делом мы открываем для чтения файл jokes.txt и инициализируем нулем внутреннюю переменную $i. Затем запускаем цикл, который должен прерваться, как только будет достигнут конец файла: while(!feof($f)). Внутри цикла мы читаем текущую строку (предположив, что ее максимальная длина не превышает 8 кб): fgets($f, 8196) и сохраняем ее во временной переменной $tmp.
Теперь начинается самое интересное. Мы выбираем случайное число в диапазоне от нуля до нашей внутренней переменной $i, в которой, как мы дальше увидим, хранится порядковый номер текущей строки: rand(0, $i++). Затем мы проверяем, не равно ли это случайное число нулю, и если равно, то записываем в переменную $string значение текущей строки (которая хранится в $tmp). Так как в функции rand() в качестве максимального значения у нас указано $i++, то сервер сначала выберет какое-то значение от 0 до $i, a потом уже увеличит $i на единицу — и, следовательно, при следующем проходе цикла $i будет снова содержать порядковый номер текущей строки (отсчет ведется с нуля).
Теория вероятностей — штука полезная!Вероятность выпадения какого-то числа (в нашем случае — нуля) из N имеющихся равна 1/N. А это означает, что при первом проходе цикла (нулевая строка) мы с вероятностью, равной единице, сохраним в переменной $string эту строку. При втором проходе — с вероятностью в 50% в переменную $string запишется первая строка. При третьем — с вероятностью 33% запишется вторая строка. И так далее. Можете сами убедиться, что такой подход действительно выбирает случайную строку из файла, и при этом память тратится только на хранение текущей строки!
Ну и, разумеется, после использования файл нужно закрыть с помощью fclose(). И в самом конце, так как функция fgets сохраняет символ перевода строки, мы его убираем с помощью функции trim().
Кстати, с помощью этого же алгоритма можно, например, выводить случайную картинку из директории — все, что требуется — это вместо открытия файла открыть директорию, а вместо чтения строки читать имя файла...
Еще одним распространенным случаем является выборка случайных записей из базы данных. Довольно часто для этого пытаются использовать оператор SELECT без указания ORDER BY, что в корне неверно: в спецификации SQL сказано, что в этом случае порядок записей будет неопределен, а не случаен! В этом легко убедиться и на практике — при последовательных выполнениях этого оператора в течение более или менее длительного времени вам будут возвращаться одни и те же данные...
Простые методы не всегда самые эффективныеКак и в случае с файлами, при работе с базой существует тривиальный и максимально неэкономный способ: выполнить SELECT, определить общее число записей и прочитать случайную запись из полученного набора. Или, что еще хуже, прочитать все записи в массив и выбрать случайный элемент. Подобный подход страшно прожорлив и крайне не рекомендуется к использованию, особенно если выборка производится из большой таблицы.
Используйте встроенные функцииЗначительно более эффективное и элегантное решение получится, если воспользоваться встроенной в СУБД функцией генератора случайных чисел — как правило, она называется random() (в PostgreSQL) или rand() (в MySQL). В других базах эта функция может вызываться по-другому — смотрите документацию. Современные версии СУБД (как минимум MySQL и PostgreSQL) понимают специальную конструкцию ORDER BY:
SELECT * FROM table ORDER BY rand()
а если требуется получить какое-то определенное число записей, то можно еще добавить оператор LIMIT:
SELECT * FROM table ORDER BY rand() LIMIT 1
Если же у вас на сервере стоит старая версия, то можно использовать другой трюк:
SELECT *, id*0+rand() as rnd ORDER BY rnd LIMIT 1
Конструкция id*0+rand() выглядит глупо, но без нее некоторые версии СУБД будут работать некорректно — они один раз высчитают случайное число, а потом добавят его ко всем записям. Чтобы этого не происходило, в выражении необходимо использовать значение какого-то поля текущей записи.
Может оказаться востребованным и генератор случайных строк — например, для того чтобы задать пароль для вновь созданной учетной записи пользователя. Очень удобно для подобных целей использовать специальную функцию, которой в качестве параметра передавать требуемую длину строки:
function randstr($length = 8) {
$a = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
for($i=0; $i<$length; $i++) $ret .= substr($a, rand(0, strlen($a)), 1);
return $ret;
}
Здесь $a — это строка разрешенных к использованию символов, а $length — длина. И использование и работа этой функции, думаю, не вызовет у вас никаких сложностей...
Ну и, наконец, еще одна функция, которая может оказаться полезной — это shuffle(). Занимается она тем, что тасует переданный ей в качестве аргумента массив, располагая его элементы «в строгом беспорядке». Использование этой функции никаких проблем или сложностей не вызывает...
Статья получена: hostinfo.ru