0

I'm trying to figure out the best way to create an array of sets of randomly selected numbers from a range. So, for instance I have a range of numbers from which to select: 1-100. I want to generate X number of sets of 5 of those numbers WITHOUT DUPLICATES. So, I want to generate something like:

[3, 24, 32, 49, 68]

[2, 18, 43, 76, 98]

[10, 12, 23, 45, 67]

[5, 56, 64, 72, 90]

...

I know how to generate random numbers from a range once, I just am stuck on doing it X number of times without the possibility of duplicate sets.

j08691
  • 204,283
  • 31
  • 260
  • 272
  • If you remove so-called "duplicate sets" then it would not be truly random. To get what you want you would generate a set from the larger set, remove the numbers from the smaller set, then generate the next set - rinse, lather, repeat. – Jay Blanchard Jan 30 '20 at 15:29
  • 1
    Just gonna answer this here, because I honestly don't think this question should have been closed: `$a = range(1, 100); shuffle($a); $sets = array_chunk($a, 5); var_dump($sets);` Cheers! – Sherif Jan 30 '20 at 15:38
  • @Sherif agreed, this shouldn't have been closed as a duplicate. Voted to reopen. And also great solution, post as an answer if it get's reopened – Brett Gregson Jan 30 '20 at 15:40
  • Reopened the question - I agree it's not a duplicate of what it was closed for. – BadHorsie Jan 30 '20 at 15:45

2 Answers2

1

One way to solve this problem is simply to generate the desired range $arr = range(1, 100), then shuffle it to randomize shuffle($arr) then chunk it to get sets of 5 $sets = array_chunk($arr, 5).

So the final answer:

$arr = range(1, 100); // gives us an array of 100 integers 1-100
shuffle($arr); // randomly orders those integers
$sets = array_chunk($arr, 5); // creats an array of arrays of sets of 5 integers

foreach ($sets as $set) {
    echo implode(',', $set), "\n";
}

Gives you something like...

14,24,60,95,86
47,54,10,77,3
11,22,88,80,39
72,46,81,78,59
63,98,52,82,8
79,34,43,13,41
67,33,75,1,2
4,57,84,73,17
32,55,35,18,70
64,85,100,93,71
25,19,26,20,76
99,89,7,87,91
37,97,68,27,5
74,48,65,61,58
45,31,9,30,21
16,56,50,96,90
92,40,6,44,23
28,94,38,83,29
36,62,51,66,15
12,69,49,42,53

Alternative implementation

Another way to solve this problem, that doesn't require writing the entire array into memory at once and then resorting it randomly, would be to use a hashset that tracks all randomly generated integers to prevent duplicates. This gives us the same exact result except that we do both the random number generation and sorting in a single step instead of two. You can also even do the chunking in the same step this way.

Keep in mind, however, that this solution is technically slower since it's in unbounded time (we have to keep guessing if the number already exists).

I'm going to use a generator to implement this solution since it's easier to write than an Iterator pattern.

function randomizeIntegerXRangeChunked($start, $end, $chunks = 5): Generator
{
    $inSet = []; // track the integers already in the set

    for ($i = $start, $c = 0; $i <= $end; $i++) {
        /* solution is in unbounded time */
        while (isset($inSet[($n = mt_rand($start, $end))]));

        $inSet[$n] = true;

        yield $c => $n;

        if (!($i % $chunks)) {
            $c++;
        }
    }
}


$arr = [];

foreach (randomizeIntegerXRangeChunked(1, 100, 5) as $chunk => $int) {

    $arr[$chunk][] = $int;

}

var_dump($arr);

The output will be similar to...

array(20) {
  [0]=>
  array(5) {
    [0]=>
    int(43)
    [1]=>
    int(52)
    [2]=>
    int(38)
    [3]=>
    int(73)
    [4]=>
    int(55)
  }
  [1]=>
  array(5) {
    [0]=>
    int(59)
    [1]=>
    int(3)
    [2]=>
    int(71)
    [3]=>
    int(47)
    [4]=>
    int(50)
  }
  [2]=>
  array(5) {
    [0]=>
    int(54)
    [1]=>
    int(19)
    [2]=>
    int(60)
    [3]=>
    int(40)
    [4]=>
    int(5)
  }
  [3]=>
  array(5) {
    [0]=>
    int(26)
    [1]=>
    int(46)
    [2]=>
    int(93)
    [3]=>
    int(80)
    [4]=>
    int(63)
  }
  [4]=>
  array(5) {
    [0]=>
    int(18)
    [1]=>
    int(45)
    [2]=>
    int(98)
    [3]=>
    int(1)
    [4]=>
    int(72)
  }
  [5]=>
  array(5) {
    [0]=>
    int(37)
    [1]=>
    int(97)
    [2]=>
    int(15)
    [3]=>
    int(68)
    [4]=>
    int(8)
  }
  [6]=>
  array(5) {
    [0]=>
    int(34)
    [1]=>
    int(14)
    [2]=>
    int(33)
    [3]=>
    int(24)
    [4]=>
    int(65)
  }
  [7]=>
  array(5) {
    [0]=>
    int(4)
    [1]=>
    int(16)
    [2]=>
    int(13)
    [3]=>
    int(41)
    [4]=>
    int(86)
  }
  [8]=>
  array(5) {
    [0]=>
    int(95)
    [1]=>
    int(12)
    [2]=>
    int(44)
    [3]=>
    int(66)
    [4]=>
    int(83)
  }
  [9]=>
  array(5) {
    [0]=>
    int(67)
    [1]=>
    int(48)
    [2]=>
    int(91)
    [3]=>
    int(27)
    [4]=>
    int(79)
  }
  [10]=>
  array(5) {
    [0]=>
    int(56)
    [1]=>
    int(25)
    [2]=>
    int(2)
    [3]=>
    int(64)
    [4]=>
    int(78)
  }
  [11]=>
  array(5) {
    [0]=>
    int(57)
    [1]=>
    int(17)
    [2]=>
    int(74)
    [3]=>
    int(42)
    [4]=>
    int(69)
  }
  [12]=>
  array(5) {
    [0]=>
    int(96)
    [1]=>
    int(20)
    [2]=>
    int(9)
    [3]=>
    int(28)
    [4]=>
    int(7)
  }
  [13]=>
  array(5) {
    [0]=>
    int(30)
    [1]=>
    int(75)
    [2]=>
    int(21)
    [3]=>
    int(6)
    [4]=>
    int(89)
  }
  [14]=>
  array(5) {
    [0]=>
    int(51)
    [1]=>
    int(36)
    [2]=>
    int(62)
    [3]=>
    int(58)
    [4]=>
    int(23)
  }
  [15]=>
  array(5) {
    [0]=>
    int(85)
    [1]=>
    int(32)
    [2]=>
    int(100)
    [3]=>
    int(61)
    [4]=>
    int(49)
  }
  [16]=>
  array(5) {
    [0]=>
    int(39)
    [1]=>
    int(87)
    [2]=>
    int(76)
    [3]=>
    int(70)
    [4]=>
    int(22)
  }
  [17]=>
  array(5) {
    [0]=>
    int(88)
    [1]=>
    int(77)
    [2]=>
    int(10)
    [3]=>
    int(99)
    [4]=>
    int(53)
  }
  [18]=>
  array(5) {
    [0]=>
    int(94)
    [1]=>
    int(35)
    [2]=>
    int(92)
    [3]=>
    int(90)
    [4]=>
    int(84)
  }
  [19]=>
  array(5) {
    [0]=>
    int(81)
    [1]=>
    int(82)
    [2]=>
    int(31)
    [3]=>
    int(29)
    [4]=>
    int(11)
  }
}
Sherif
  • 11,786
  • 3
  • 32
  • 57
0

You can try it like this as well.

// Array
$NumberArray = array();
$TempArray = array(); // Used for storing randoms while we check via if statement.

// Loop 5 times.
for ($x=0; $x<=4; $x++) {

    // Random Number 1-100
    $RandomNumber = rand(1,100);

    // We can use Array Push to add to the array and check using in_array.
    array_push($TempArray, $RandomNumber);

    // Check first thing in temp array and see if its already in number array. (If not then push)
    if (!in_array(reset($TempArray), $NumberArray)) { 
        array_push($NumberArray, reset($TempArray)); // Add to NumberArray
        // Clear Temp Array
        $TempArray = array();
    } else {
        // Add to the loop
        $x--;
    }

}

print_r($NumberArray);

This outputs an array like such:

Array
(
    [0] => 32
    [1] => 26
    [2] => 59
    [3] => 96
    [4] => 34
)
Babyhamsta
  • 43
  • 5