Как получить случайное значение от 1~N, но исключая несколько конкретных значений в PHP?
rand(1,N)
но исключая array(a,b,c,..)
,
Есть ли уже встроенная функция, которую я не знаю, или мне нужно реализовать ее самостоятельно (как?)?
ОБНОВЛЕНИЕ
Квалифицированное решение должно обладать высокой производительностью независимо от того, большой размер excluded array
или нет.
7 answers
Встроенной функции нет, но вы могли бы сделать это:
function randWithout($from, $to, array $exceptions) {
sort($exceptions); // lets us use break; in the foreach reliably
$number = rand($from, $to - count($exceptions)); // or mt_rand()
foreach ($exceptions as $exception) {
if ($number >= $exception) {
$number++; // make up for the gap
} else /*if ($number < $exception)*/ {
break;
}
}
return $number;
}
Это не укладывается у меня в голове, так что его можно было бы отполировать - но, по крайней мере, вы не можете оказаться в сценарии с бесконечным циклом, даже гипотетически.
Примечание: Функция прерывается, если $exceptions
исчерпывает ваш диапазон - например, вызов randWithout(1, 2, array(1,2))
или randWithout(1, 2, array(0,1,2,3))
не даст ничего разумного (очевидно), но в этом случае возвращаемый номер будет находиться за пределами $from
-$to
диапазон, так что легко поймать.
Если $exceptions
гарантированно уже отсортирован, sort($exceptions);
можно удалить.
Леденец для глаз: Несколько минималистичная визуализация алгоритма.
Я не думаю, что есть такая встроенная функция; вам, вероятно, придется кодировать ее самостоятельно.
Чтобы закодировать это, у вас есть два решения:
- Используйте цикл, чтобы вызвать rand() или mt_rand(), пока он не вернет правильное значение
- что означает вызов rand() несколько раз, в худшем случае
- но это должно работать нормально, если N большое, и у вас не так много запрещенных значений.
- Создайте массив, содержащий только допустимые значения
- И использовать
array_rand
чтобы выбрать из него одно значение - который будет работать нормально, если N мало
- И использовать
В зависимости от того, что именно вам нужно и почему, этот подход может быть интересной альтернативой.
$numbers = array_diff(range(1, N), array(a, b, c));
// Either (not a real answer, but could be useful, depending on your circumstances)
shuffle($numbers); // $numbers is now a randomly-sorted array containing all the numbers that interest you
// Or:
$x = $numbers[array_rand($numbers)]; // $x is now a random number selected from the set of numbers you're interested in
Итак, если вам не нужно каждый раз генерировать набор потенциальных чисел, но вы генерируете набор один раз, а затем выбираете набор случайных чисел из одного и того же набора, это может быть хорошим способом.
Самый простой способ...
<?php
function rand_except($min, $max, $excepting = array()) {
$num = mt_rand($min, $max);
return in_array($num, $excepting) ? rand_except($min, $max, $excepting) : $num;
}
?>
Что вам нужно сделать, так это вычислить массив пропущенных местоположений, чтобы вы могли выбрать случайную позицию в непрерывном массиве длины M = N - #of exceptions
и легко сопоставить ее с исходным массивом с отверстиями. Для этого потребуется время и пространство, равные пропущенному массиву. Я не отличаю php от дыры в земле, так что простите за текстовый пример кода полу-psudo.
- Сделайте новое смещение массива[] такой же длины, как и массив исключений.
- в смещении[i] сохраняем первый индекс в воображаемый массив без отверстий, в котором были бы пропущены элементы
i
в исходном массиве. - Теперь нужно выбрать случайный элемент. Выберите случайное число
r
в0..M
количестве оставшихся элементов. - Найдите
i
такое, чтоOffset[i] <= r < Offest[i+i]
это легко с помощью двоичного поиска - Возврат
r + i
Теперь это всего лишь набросок, вам нужно будет разобраться с концами массивов, и если все проиндексировано в виде 0 или 1 и все такое прочее. Если вы умны, вы действительно можете вычислить массив смещений на лету отличается от оригинала, хотя это немного менее понятно.
Возможно, уже слишком поздно для ответа, но я нашел этот фрагмент кода где-то в своем уме, когда пытался получить случайные данные из базы данных на основе случайного идентификатора, исключая какое-то число.
$excludedData = array(); // This is your excluded number
$maxVal = $this->db->count_all_results("game_pertanyaan"); // Get the maximum number based on my database
$randomNum = rand(1, $maxVal); // Make first initiation, I think you can put this directly in the while > in_array paramater, seems working as well, it's up to you
while (in_array($randomNum, $excludedData)) {
$randomNum = rand(1, $maxVal);
}
$randomNum; //Your random number excluding some number you choose
Это самый быстрый и лучший по производительности способ сделать это:
$all = range($Min,$Max);
$diff = array_diff($all,$Exclude);
shuffle($diff );
$data = array_slice($diff,0,$quantity);