Search code examples
phparraysrandomfilterduplicates

How to get n number of random values from an array and prevent consecutively repeated values


I want to populate a result array containing values randomly drawn from an input array, but the result array must not have two identical consecutive values.

Additional rules:

  1. The input array of values will contain only unique values and will have at least two values to ensure that it is possible to populate the required result array.
  2. The number of random values may be more or less than the size of the input array.
  3. The result array must not require that all values from the input are used if the number of random values is greater than the input array's size. In other words, the randomly selected values must not be biased for even distribution.

Sample input:

$array = ['one', 'two', 'three', 'four'];
$n = 10;

A non-exhaustive list of possible valid results:

  • ["three","one","three","one","two","one","four","one","three","four"]

  • ["four","three","two","one","two","four","one","three","two","one"]

  • ["two","four","three","one","two","one","four","two","three","one"]

How can I do this?


This question was inspired by this deleted question which struggled to ask the question with clear rules and expectations.


Solution

  • To guarantee that the two consecutive values are not the same, keep track of the previous value (or its key) and remove it as a possible random value for the current iteration. Push the random value into the result array, then update the "previous" variable.

    array_diff_key() can be used to exclude a specific key before calling array_rand() to return the random key.

    Code: (Demo) (Reduced alternative) (The ugly version)

    $lastIndex = -1;
    $result = [];
    for ($x = 0; $x < $n; ++$x) {
        $key = array_rand(array_diff_key($array, [$lastIndex => null]));
        $result[] = $array[$key];
        $lastIndex = $key;
    }
    echo json_encode($result);
    

    Alternatively, you can use unset() to exclude the previous random value, but it is important to not modify the original array or else there may not be enough values to fill the result array. Modifying a copy of the input array will do.

    Code: (Demo)

    $lastIndex = -1;
    $result = [];
    for ($x = 0; $x < $n; ++$x) {
        $copy = $array;
        unset($copy[$lastIndex]);
        $key = array_rand($copy);
        $result[] = $copy[$key];
        $lastIndex = $key;
    }
    echo json_encode($result);
    

    A brute force script can guess, check, and overwrite a pushed consecutive duplicate value -- this will not have a finite number of loops. Consecutive duplicates will be increasingly probable with smaller input arrays.

    In the loop, unconditionally push the randomly fetched value into the result array, then only conditionally increment the counter variable if the result array has a solitary value or the last two values are different. (Demo)

    $result = [];
    for ($x = 0; $x < $n; $x += (int) (!$x || $result[$x] !== $result[$x - 1])) {
        $result[$x] = $array[array_rand($array)];
    }
    echo json_encode($result);