Pricop George-Tiberiu
Pricop George-Tiberiu

Reputation: 35

Generate x random numbers from specific range which doesn't duplicate and are not in a array

How I could generate a list of numbers (18 numbers), which are in a range which starts with 1 and ends with 36 (including 1 and 36), and the generated numbers must be different from numbers which exist in an array.

Example: Array numbers: 27, 31, 18, 4, 15, 6

I need to output 18 random numbers which aren't in above array, and the numbers must not be duplicated.

Output I should get: 17, 21, 14, 28, 7, 8, 12, 20, 3, 5, 16, 2, 36, 11, 26, 13, 24, 35

function randomGen($min, $max, $quantity) {
    $exluding_array = [1,2,3,4,5,6,7,8,9,10];
    $numbers = range($min, $max);
    shuffle($numbers);
    if(range($min, $max) === $exluding_array) {
        return "no";
    } else {
        return array_slice($numbers, 0, $quantity);
    }
}

With this code I get random numbers without duplicates, but the returned numbers can be numbers from exluding_array.

Upvotes: 0

Views: 69

Answers (3)

Jeto
Jeto

Reputation: 14927

I would make use of array_filter to filter out the excluded numbers:

function randomGen(int $min, int $max, int $quantity, array $excluded = []): array
{
  $result = array_filter(range($min, $max), static function ($val) use ($excluded) {
    return !in_array($val, $excluded, true);
  });
  shuffle($result);
  return array_slice($result, 0, $quantity);
}

$result = randomGen(1, 36, 10, range(1, 10));

Demo: https://3v4l.org/nkbTK

Upvotes: 0

Marvin
Marvin

Reputation: 14255

You can use array_diff to remove all numbers of excluding_array from the generated numbers before slicing:

function randomGen($min, $max, $quantity) {
    $excluding_array = [1,2,3,4,5,6,7,8,9,10];
    $numbers = range($min, $max);
    // Remove unwanted numbers
    $numbers = array_diff($numbers, $excluding_array);
    shuffle($numbers);
    if (count($numbers) < $quantity) {
        return "no";
    } else {
        return array_slice($numbers, 0, $quantity);
    }
}

Note: I also improved your sanity check a bit, although returning "no" doesn't seem to be the best solution in that case. You might want to throw an exception as shown by vivek_23.

(Sample on PHP sandbox)

Upvotes: 1

nice_dev
nice_dev

Reputation: 17805

Another approach is you can array_flip the $excluding array and check for these numbers existence in $numbers. If we find them, we skip them, else we include it in our final array.

We could also throw an Exception if we can't produce as many numbers as $quantity.

<?php

function randomGen($min, $max, $quantity) {
    $exluding_array = [1,2,3,4,5,6,7,8,9,10];
    $exluding_array = array_flip($exluding_array);
    $numbers = range($min, $max);
    shuffle($numbers);
    $filtered_numbers = [];
    foreach($numbers as $curr_number){
        if(isset($exluding_array[$curr_number])) continue;
        $filtered_numbers[] = $curr_number;
    }

    if(count($filtered_numbers) < $quantity) throw new Exception("Can't generate numbers with the given quantity and range.");

    return array_slice($filtered_numbers, 0, $quantity);
}


print_r(randomGen(1,36,10));

Demo: https://3v4l.org/mUi8A

You can also make the callee of this function implement a try-catch block to handle the exception gracefully.

Upvotes: 0

Related Questions