Как получить случайное значение от 1 до N, но исключая несколько конкретных значений в PHP? - PullRequest
6 голосов
/ 23 апреля 2010

rand(1,N), но исключая array(a,b,c,..),

. Есть ли уже встроенная функция, которую я не знаю, или я должен сам ее реализовать (как?)?

ОБНОВЛЕНИЕ

Квалифицированное решение должно иметь производительность золота независимо от того, большой размер excluded array или нет.

Ответы [ 7 ]

16 голосов
/ 23 апреля 2010

Нет встроенной функции, но вы могли бы сделать это:

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); можно удалить.

Eye-candy : Несколько минималистичная визуализация алгоритма .

9 голосов
/ 23 апреля 2010

Я не думаю, что есть такая встроенная функция;вам, вероятно, придется кодировать его самостоятельно.

Чтобы закодировать это, у вас есть два решения:

  • Используйте цикл для вызова rand () или mt_rand (), пока он не вернетсяправильное значение
    • , что означает вызов rand () несколько раз, в худшем случае
    • , но это должно работать нормально, если N большое, и у вас нет много запрещенных значений.
  • Создайте массив, содержащий только допустимые значения
    • И используйте array_rand, чтобы выбрать из него одно значение
    • , который будет нормально работать, если N мало
7 голосов
/ 23 апреля 2010

В зависимости от того, что именно вам нужно и почему, этот подход может быть интересной альтернативой.

$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

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

4 голосов
/ 23 апреля 2010

Самый простой способ ...

<?php

function rand_except($min, $max, $excepting = array()) {

    $num = mt_rand($min, $max);

    return in_array($num, $excepting) ? rand_except($min, $max, $excepting) : $num;
}
?>
1 голос
/ 23 апреля 2010

Вам нужно вычислить массив пропущенных местоположений, чтобы вы могли выбрать случайную позицию в непрерывном массиве длиной M = N - #of exceptions и легко сопоставить ее с исходным массивом с отверстиями. Это потребует времени и пространства, равного пропущенному массиву. Я не знаю php из дыры в земле, так что простите текстовый пример полупсудо кода.

  1. Создать новый массив Offset [] той же длины, что и массив Exceptions.
  2. в Offset [i] хранит первый индекс в воображаемом не дырявом массиве, который пропустил бы i элементов в исходном массиве.
  3. Теперь выберите случайный элемент. Выберите случайное число, r, в 0..M количество оставшихся элементов.
  4. Найти i такой, что Offset[i] <= r < Offest[i+i] это легко с бинарным поиском
  5. Возврат r + i

Теперь, это просто набросок, который вам понадобится, чтобы иметь дело с концами массивов, и если вещи проиндексированы от 0 или 1 и всего этого джаза. Если вы сообразительны, вы можете вычислить массив смещений на лету из оригинала, хотя это немного менее понятно.

0 голосов
/ 20 сентября 2016

Это самый быстрый и лучший производительность способ сделать это:

$all =  range($Min,$Max);
$diff = array_diff($all,$Exclude);
shuffle($diff );
$data = array_slice($diff,0,$quantity);
0 голосов
/ 11 марта 2015

Возможно, уже слишком поздно для ответа, но я нашел этот фрагмент кода где-то в уме, когда пытался получить случайные данные из базы данных на основе случайного идентификатора, исключая некоторое число.

$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
...