10

I want to select a random value from a array, but keep it unique as long as possible.

For example if I'm selecting a value 4 times from a array of 4 elements, the selected value should be random, but different every time.

If I'm selecting it 10 times from the same array of 4 elements, then obviously some values will be duplicated.

I have this right now, but I still get duplicate values, even if the loop is running 4 times:

$arr = $arr_history = ('abc', 'def', 'xyz', 'qqq');

for($i = 1; $i < 5; $i++){
  if(empty($arr_history)) $arr_history = $arr; 
  $selected = $arr_history[array_rand($arr_history, 1)];  
  unset($arr_history[$selected]); 
  // do something with $selected here...
}
3
  • 1
    On a side note, nsftools.com/misc/DilbertRandom.gif Commented Jun 24, 2011 at 17:28
  • 3
    Why don't you use array_rand($arr_history, 4) to get the array in a random order? This ensures the that you have 4 unique values. In the next iteration you get the array in a different order. Commented Jun 24, 2011 at 17:29
  • The statement unset($arr_history[$selected]); seems to treat $arr_history as an associative array, since $selected is the value, not the index. That doesn't solve the problem, though. Unsetting $selected is supposed to remove it from the array, which implies that by the time the loop s done, the array is empty. Was that your intention? (I don't think so) Commented Jun 24, 2011 at 17:34

9 Answers 9

13

You almost have it right. The problem was the unset($arr_history[$selected]); line. The value of $selected isn't a key but in fact a value so the unset wouldn't work.

To keep it the same as what you have up there:

<?php

$arr = $arr_history = array('abc', 'def', 'xyz', 'qqq');

for ( $i = 1; $i < 10; $i++ )
{
  // If the history array is empty, re-populate it.
  if ( empty($arr_history) )
    $arr_history = $arr;

  // Select a random key.
  $key = array_rand($arr_history, 1);

  // Save the record in $selected.
  $selected = $arr_history[$key];

  // Remove the key/pair from the array.
  unset($arr_history[$key]);

  // Echo the selected value.
  echo $selected . PHP_EOL;
}

Or an example with a few less lines:

<?php

$arr = $arr_history = array('abc', 'def', 'xyz', 'qqq');

for ( $i = 1; $i < 10; $i++ )
{
  // If the history array is empty, re-populate it.
  if ( empty($arr_history) )
    $arr_history = $arr;

  // Randomize the array.
  array_rand($arr_history);

  // Select the last value from the array.
  $selected = array_pop($arr_history);

  // Echo the selected value.
  echo $selected . PHP_EOL;
}
Sign up to request clarification or add additional context in comments.

3 Comments

thanks that seems to work, but it's weird that a array like this has keys. I thought that both key and value would be the same
@Alex - No, in fact, when you don't specify the keys, PHP assigns them numeric keys starting at 0. See the parameters section of http://ca3.php.net/manual/en/function.array.php for more information.
It's alway the same result, no random
7

How about shuffling the array, and popping items off.

When pop returns null, reset the array.

$orig = array(..);
$temp = $orig;
shuffle( $temp );

function getNextValue()
{
  global $orig;
  global $temp;

  $val = array_pop( $temp );

  if (is_null($val))
  {
    $temp = $orig;
    shuffle( $temp );
    $val = getNextValue();
  }
  return $val;
}

Of course, you'll want to encapsulate this better, and do better checking, and other such things.

Comments

4

http://codepad.org/sBMEsXJ1

<?php

    $array = array('abc', 'def', 'xyz', 'qqq');

    $numRandoms = 3;

    $final = array();

    $count = count($array);

    if ($count >= $numRandoms) {

        while (count($final) < $numRandoms) {

            $random = $array[rand(0, $count - 1)];

            if (!in_array($random, $final)) {

                array_push($final, $random);
            }
        }
    }

    var_dump($final);

?>

Comments

3

Php has a native function called shuffle which you could use to randomly order the elements in the array. So what about this?

$arr = ('abc', 'def', 'xyz', 'qqq');

$random = shuffle($arr);

foreach($random as $number) {
    echo $number;
}

1 Comment

shuffle actually returns a boolean and modifies the original array so you would want foreach ($arr as $a)
2

key != value, use this:

$index = array_rand($arr_history, 1);
$selected = $arr_history[$index];  
unset($arr_history[$index]); 

Comments

1

I've done this to create a random 8 digit password for users:

$characters = array(
    "A","B","C","D","E","F","G","H","J","K","L","M",
    "N","P","Q","R","S","T","U","V","W","X","Y","Z",
    "a","b","c","d","e","f","g","h","i","j","k","m",
    "n","p","q","r","s","t","u","v","w","x","y","z",
    "1","2","3","4","5","6","7","8","9");

for( $i=0;$i<=8;++$i ){
    shuffle( $characters );
    $new_pass .= $characters[0];
}

3 Comments

and you don't get duplicate characters at all?
Or just: substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 8) which would be cheaper.
@Alex - yes you do. @Leif - that is nicer you are right, thanks for the insight!
1

If you do not care about what particular values are in the array, you could try to implement a Linear Congruential Generator to generate all the values in the array.

LCG implementation

Wikipedia lists some values you can use, and the rules to select the values for the LCG algorithm, because the LCG algorith is deterministic it is guaranteed not to repeat a single value before the length of the period.

After filling the array with this unique numbers, you can simply get the numbers in the array 1 by 1 in order.

Comments

1
$isShowCategory = array();
for ($i=0; $i <5 ; $i++) { 
   $myCategory = array_rand($arrTrCategoryApp,1); 
   if (!in_array($myCategory, $isShowCategory)) {
      $isShowCategory[] = $myCategory;

      #do something 

   }
}

1 Comment

adding some context around your answer (eg why it solves the problem) would help other people looking at the answer.
-1

Easy and clean:

$colors = array('blue', 'green', 'orange');
$history = $colors;

function getColor($colors, &$history){
    if(count($history)==0)
        $history = $colors;
    return array_pop( $history );
}

echo getColor($colors, $history);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.