从PHP数组中获取随机值,但要确保唯一性。

10

我想从数组中随机选择一个值,但尽可能保持唯一。

例如,如果我从一个由4个元素组成的数组中选择一个值4次,则所选值应该是随机的,但每次都不同。

如果我从相同的4个元素组成的数组中选择它10次,显然有些值将重复。

我现在有这个代码,但即使循环运行4次,我仍然会得到重复的值:

$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...
}

1
顺便提一下,http://www.nsftools.com/misc/DilbertRandom.gif - Kumar
3
为什么不使用 array_rand($arr_history, 4) 来随机获取数组?这样可确保您获得 4 个唯一值。在下一次迭代中,您将以不同的顺序获取该数组。 - Felix Kling
语句 unset($arr_history[$selected]); 看起来将 $arr_history 视为关联数组,因为 $selected 是值而不是索引。然而,这并没有解决问题。取消设置 $selected 应该将其从数组中删除,这意味着在循环结束时,数组为空。这是你的意图吗?(我认为不是) - afaolek
9个回答

13

你几乎做对了。问题在于 unset($arr_history[$selected]); 这一行代码。变量 $selected 的值并不是一个键,而是一个值,所以 unset 操作无法生效。

为了保持与上面的代码相同:

<?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;
}

或者一个更简短的例子:

<?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;
}

谢谢,这似乎有效,但是像这样的数组有键值对确实很奇怪。我以为键和值应该是相同的。 - Alex
2
@Alex - 不,事实上,当你不指定键时,PHP会为它们分配从0开始的数字键。有关更多信息,请参阅http://ca3.php.net/manual/en/function.array.php中的参数部分。 - Francois Deschenes
始终是相同的结果,没有随机性。 - Gino

7
如何对数组进行洗牌,然后弹出其中的项。
pop 返回 null 时,重置数组。
$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;
}

当然,您会希望更好地封装它,并进行更好的检查和其他类似的操作。


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);

?>

3

Php有一个名为shuffle的本地函数,可以用来随机排列数组中的元素。那么这个怎么样呢?

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

$random = shuffle($arr);

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

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

2

如果键值不相等,请使用以下代码:

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

1
我已经做了这个来为用户创建一个随机的8位密码:
$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];
}

你能确保没有重复字符吗? - Alex
2
或者只需要使用 substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 8) 这个方法,这样更便宜。 - Leif
@Alex - 是的,你需要。@Leif - 那很好,你是对的,感谢你的见解! - sadmicrowave

1

如果您不关心数组中的特定值,可以尝试实现线性同余生成器来生成数组中的所有值。

LCG 实现

维基百科列出了一些可用的值以及选择 LCG 算法值的规则,因为 LCG 算法是确定性的,它保证在周期长度之前不会重复任何一个值。

在使用这些唯一数字填充数组后,您可以按顺序逐个获取数组中的数字。


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

      #do something 

   }
}

提供一些上下文信息有助于其他人查看答案时了解问题的解决方法。 - sbkrogers

-1

简单干净:

$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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接