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:
- 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.
- The number of random values may be more or less than the size of the input array.
- 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"]
This question was inspired by this deleted question which struggled to ask the question with clear rules and expectations.
2
Answers
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 callingarray_rand()
to return the random key.Code: (Demo) (Reduced alternative) (The ugly version)
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)
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)
Brute force approach
It is possible to do this with a
while
loop to compare the current randomly chosen element of the input$array
with the last element of the$result
array (using the PHPend()
function).This method will result in more calls to
array_rand()
than the number of required elements to be returned, however it is not necessary to create a copy of the original array.However, the number of extra loops will be connected to the length of
$array
, therefore when$array
has:This is clearly visible when we increase the start value of
$n
to about 1000.As the length of
$array
increases, the expected percentage of extra loops will tend to 0 though obviously never reaching 0.Code: Demo
Finite number of loops approach
There are several ways to ensure that there are a finite number of loops as given in another answer to this question but they involve creating a copy of
$array
for each loop, which may or may not be an issue.The following method also creates copies of
$array
but rather than creating a new copy of$array
on each loop, this approach creates a map of arrays and looks up to see if the array has already been created.Code: Demo