Генерация УНИКАЛЬНЫХ случайных чисел в заданном диапазоне
Заголовок: Как сгенерировать уникальные случайные числа в заданном диапазоне?
Тело вопроса: Мне нужно сгенерировать уникальные случайные числа в заданном диапазоне, как я могу это сделать? Я уже могу генерировать случайные числа с помощью следующего кода:
generator:
$arr = [];
$x = rand($min, $max);
$len = count($arr);
$flag = 0;
for($i = 0; $i < $len; $i++) {
if ($flag === 1)
goto generator;
if ($x === $arr[$i])
$flag = 1;
}
$arr[$index] = $x;
$index++;
goto generator;
Я понимаю, что этот код неэффективен, поэтому мне нужен более оптимизированный вариант! Пожалуйста, помогите!
Пример: Если мне нужно сгенерировать 3 уникальных числа в диапазоне от 1 до 15, они должны быть такими, как 5, 9, 1, но не 3, 1, 2 (где 3 — количество чисел, которые я хочу сгенерировать).
5 ответ(ов)
Для генерации массива уникальных случайных чисел в заданном диапазоне можно воспользоваться функцией range()
для создания массива чисел, а затем перемешать его с помощью функции shuffle()
. Ниже представлен пример функции, которая принимает минимальное и максимальное значения, а также количество требуемых случайных чисел.
Пример функции
function UniqueRandomNumbersWithinRange($min, $max, $quantity) {
$numbers = range($min, $max); // Создаём массив чисел от $min до $max
shuffle($numbers); // Перемешиваем массив случайным образом
return array_slice($numbers, 0, $quantity); // Возвращаем заданное количество уникальных чисел
}
Использование функции
<?php
print_r(UniqueRandomNumbersWithinRange(0, 25, 5));
?>
Возможный результат
В результате выполнения приведенного кода вы можете получить массив с 5 уникальными случайными числами в диапазоне от 0 до 25, например:
Array
(
[0] => 14
[1] => 16
[2] => 17
[3] => 20
[4] => 1
)
Таким образом, данная функция позволяет легко генерировать необходимое количество уникальных случайных чисел в указанном вами диапазоне. Обратите внимание, что если запрашиваемое количество чисел превышает доступный диапазон, это может привести к ошибке или неожиданным результатам.
Можно перевести данный ответ на русский язык в стиле StackOverflow следующим образом:
Я был заинтересован в том, как принятый ответ сравнивается с моим. Полезно отметить, что гибридный подход, объединяющий оба решения, может быть выгодным; по сути, функция, которая условно использует одно или другое в зависимости от определённых значений, может быть лучшим выбором:
# Принятый ответ
function randRange1($min, $max, $count)
{
$numbers = range($min, $max);
shuffle($numbers);
return array_slice($numbers, 0, $count);
}
# Мой ответ
function randRange2($min, $max, $count)
{
$i = 0;
$range = array();
while ($i++ < $count) {
while(in_array($num = mt_rand($min, $max), $range));
$range[] = $num;
}
return $range;
}
echo 'randRange1: малый диапазон, высокое количество' . PHP_EOL;
$time = microtime(true);
randRange1(0, 9999, 5000);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
echo 'randRange2: малый диапазон, высокое количество' . PHP_EOL;
$time = microtime(true);
randRange2(0, 9999, 5000);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
echo 'randRange1: широкий диапазон, малое количество' . PHP_EOL;
$time = microtime(true);
randRange1(0, 999999, 6);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
echo 'randRange2: широкий диапазон, малое количество' . PHP_EOL;
$time = microtime(true);
randRange2(0, 999999, 6);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;
Результаты:
randRange1: малый диапазон, высокое количество
0.019910097122192
randRange2: малый диапазон, высокое количество
1.5043621063232
randRange1: широкий диапазон, малое количество
2.4722430706024
randRange2: широкий диапазон, малое количество
0.0001051425933837
Как видно, если вы используете меньший диапазон и большее количество возвращаемых значений, принятый ответ определенно является оптимальным. Однако, как я и ожидал, для более широких диапазонов и меньших количеств значений принятому ответу потребуется намного больше времени, так как он должен хранить каждое возможное значение в диапазоне. Вы даже рискуете превысить лимит памяти PHP. Гибридный подход, который оценивает отношение между диапазоном и количеством, и условно выбирает генератор, мог бы стать наилучшим вариантом.
Идея заключается в том, чтобы использовать ключи массива: когда значение уже присутствует в ключах массива, размер массива остается прежним. Вот пример функции:
function getDistinctRandomNumbers($nb, $min, $max) {
if ($max - $min + 1 < $nb)
return false; // или выбросить исключение
$res = array();
do {
$res[mt_rand($min, $max)] = 1;
} while (count($res) !== $nb);
return array_keys($res);
}
Плюсы: таким образом можно избежать использования in_array
и не создавать огромный массив. Это делает функцию быстрой и экономит много памяти.
Минусы: по мере уменьшения соотношения (объем/количество) скорость снижается (но остается приемлемой). При одинаковом соотношении относительная скорость увеличивается с увеличением диапазона. ()*
() Я понимаю этот факт, так как при первом выборе свободных целых чисел больше (особенно на начальных этапах), но если у кого-то есть математическая формула, описывающая это поведение, буду рад её увидеть, не стесняйтесь делиться.*
Заключение: лучшая "универсальная" функция, похоже, является смесью между этой функцией и функцией @Anne, которая более эффективна при небольшом соотношении. Эта функция должна переключаться между двумя подходами, когда требуется определенное количество и достигается заданное соотношение (объем/количество). Поэтому нужно учесть сложность/время теста для определения этого момента.
Если вам нужно сгенерировать 100 случайных чисел, при этом каждое число должно появляться только один раз, хорошим решением будет создать массив с числами в порядке, а затем перемешать его.
Пример на PHP:
$arr = array();
for ($i=1; $i<=101; $i++) {
$arr[] = $i;
}
shuffle($arr);
print_r($arr);
На выходе вы получите нечто подобное:
Array
(
[0] => 16
[1] => 93
[2] => 46
[3] => 55
[4] => 18
[5] => 63
[6] => 19
[7] => 91
[8] => 99
[9] => 14
[10] => 45
[11] => 68
[12] => 61
[13] => 86
[14] => 64
[15] => 17
[16] => 27
[17] => 35
[18] => 87
[19] => 10
[20] => 95
[21] => 43
[22] => 51
[23] => 92
[24] => 22
[25] => 58
[26] => 71
[27] => 13
[28] => 66
[29] => 53
[30] => 49
[31] => 78
[32] => 69
[33] => 1
[34] => 42
[35] => 47
[36] => 26
[37] => 76
[38] => 70
[39] => 100
[40] => 57
[41] => 2
[42] => 23
[43] => 15
[44] => 96
[45] => 48
[46] => 29
[47] => 81
[48] => 4
[49] => 33
[50] => 79
[51] => 84
[52] => 80
[53] => 101
[54] => 88
[55] => 90
[56] => 56
[57] => 62
[58] => 65
[59] => 38
[60] => 67
[61] => 74
[62] => 37
[63] => 60
[64] => 21
[65] => 89
[66] => 3
[67] => 32
[68] => 25
[69] => 52
[70] => 50
[71] => 20
[72] => 12
[73] => 7
[74] => 54
[75] => 36
[76] => 28
[77] => 97
[78] => 94
[79] => 41
[80] => 72
[81] => 40
[82] => 83
[83] => 30
[84] => 34
[85] => 39
[86] => 6
[87] => 98
[88] => 8
[89] => 24
[90] => 5
[91] => 11
[92] => 73
[93] => 44
[94] => 85
[95] => 82
[96] => 75
[97] => 31
[98] => 77
[99] => 9
[100] => 59
)
Таким образом, вы получите массив с 100 уникальными случайными числами.
Если вам нужно получить 5 случайных чисел в диапазоне от 1 до 15, вы можете использовать следующий код:
var_dump(getRandomNumbers(1, 15, 5));
function getRandomNumbers($min, $max, $count)
{
if ($count > (($max - $min) + 1)) {
return false;
}
$values = range($min, $max);
shuffle($values);
return array_slice($values, 0, $count);
}
Этот код сначала проверяет, не превышает ли запрашиваемое количество случайных чисел доступный диапазон. Если count
больше, чем количество чисел в диапазоне, функция вернет false
. В противном случае она создаст массив чисел в указанном диапазоне, перемешает их с помощью shuffle()
и вернет первые count
значений.
Функции startsWith() и endsWith() в PHP
Как получить расширение файла в PHP?
Как читать большой файл построчно?
ReCaptcha 2.0 с использованием AJAX
Почему нельзя вызывать абстрактные функции из абстрактных классов в PHP?