PHP - 打乱数组的部分元素

4

我有一个数组,如下所示:

$animals = array (
    'giraffe',
    'lion',
    'hippo',
    'dog',
    'cat',
    'rabbit',
    'fly',
    'hamster',
    'gerbil'
    'goldfish'
);

除了 仓鼠沙鼠,我希望数组按照指定顺序排列。

对于它们之间的顺序,我想随机选择一个。 我知道可以使用以下代码:

shuffle($animals);

将它们全部随机化,但我只想对这两个随机。所以如果我执行 print_r($animals),有时也许会先得到仓鼠再得到沙鼠,但另一次则可能是先得到沙鼠再得到仓鼠。


2
将以下与编程有关的内容从英语翻译成中文。仅返回翻译的文本:注明其位置,删除它们,乱序排列,然后再放回去。 - Tymoteusz Paul
这两个值应该在整个数组中随机化,还是只在它们自己的位置上随机化? - BenM
1
你想要在数组中将“仓鼠”和“沙漏鼠”交换50%的时间,还是想随机它们的位置? - Halcyon
我想要随机它们的位置,而不是直接进行50/50的分割,并且我想要按照它们自己的位置而不是整个数组来随机它们。 - odd_duck
@odd_duck:所以在这种情况下,您想要洗牌数组的第7个和第8个索引? - Amal Murali
4个回答

9
您可以使用 splice 函数将这两个元素从数组中删除,然后随机排列它们并将它们放回原始数组:
$sub = array_splice($animals, 7, 2);
shuffle($sub);
array_splice($animals, 7, 0, $sub);
var_dump($animals);

1
@BenM 这是一个回答,提供了解决问题的思路。尽管它没有涉及实际的代码部分,但仍然是一个有效的答案,因为提问者具有实现解决方案所需的适当知识。 - AlexL
3
我完全同意Alex的观点,仅仅因为它没有提供复制/粘贴的解决方案,并不意味着它就不是一个好答案。实际上,我更喜欢这样的回答,因为它仍然需要用户自己动手去做一些工作。 - Tymoteusz Paul
好的,我不明白我的原始评论去哪了,但当我评论时,除了开头句子之外,实际上什么都没有。无论如何,现在这是一个有效的答案 :) - BenM
+1,对于其他方法,这种方法的效率有什么想法吗?例如,仅洗牌索引(所需)然后仅对原始数组进行排列/重新排列? - Nikos M.
@NikosM。它只对两个项目进行洗牌,然后将它们放回原始数组。所以我认为它非常高效,除非你想重新发明轮子。 - AlexL
显示剩余2条评论

1

添加2个变化版本的费歇尔-耶茨-科诺斯无偏置洗牌算法,只包括索引或排除索引(类似PHP的伪代码)。

function shuffle_include( $a, $inc ) 
{
    // $a is array to shuffle
    // $inc is array of indices to be included only in the shuffle
    // all other elements/indices will remain unaltered

    // fisher-yates-knuth shuffle variation O(n)
    $N = count($inc);
    while ( $N-- )
    { 
        $perm = rnd( 0, $N ); 
        $swap = $a[ $inc[$N] ]; 
        $a[ $inc[$N] ] = a[ $inc[$perm] ]; 
        $a[ $inc[$perm] ] = $swap; 
    }
    // in-place
    return $a;
}

function shuffle_exclude( $a, $exc ) 
{
    // $a is array to shuffle
    // $exc is array of indices to be excluded from the shuffle
    // all other elements/indices will be shuffled
    // assumed excluded indices are given in ascending order
    $inc = array();
    $i=0; $j=0; $l = count($a); $le = count($exc)
    while ($i < $l)
    {
        if ($j >= $le || $i<$exc[$j]) $inc[] = $i;
        else $j++;
        $i++;
    }
    // rest is same as shuffle_include function above

    // fisher-yates-knuth shuffle variation O(n)
    $N = count($inc);
    while ( $N-- )
    { 
        $perm = rnd( 0, $N ); 
        $swap = $a[ $inc[$N] ]; 
        $a[ $inc[$N] ] = $a[ $inc[$perm] ]; 
        $a[ $inc[$perm] ] = $swap; 
    }
    // in-place
    return $a;
}

例子:

$a = array(1,2,3,4,5,6);

print_r( shuffle_include( $a, array(0,1,2) ) );
// sample output: [2,1,3,4,5,6] , only 0,1,2 indices are shuffled

print_r( shuffle_exclude( $a, array(0,1,2) ) );
// sample output: [1,2,3,6,5,4], all other indices are shuffled except 0,1,2

注意 PHP的shuffle函数本身使用了Fisher-Yates-Knuth洗牌算法的变体。

注意2 所有提供的洗牌算法(以及PHP的原始洗牌函数)都具有(平均)时间复杂度为$O(n)$(n=要洗牌的数组大小)。

有关shuffle的其他变体,请参见:

  1. 从PHP数组中高效地选择n个随机元素(无需洗牌)

0
$animals = array (
'giraffe',
'lion',
'hippo',
'dog',
'cat',
'rabbit',
'fly',
'goldfish'
);
$other = array('hamster','gerbil');
$allAnimals = array();
foreach($animals as $key => $animal){
    if($key == 7){
        $allAnimals = array_merge($allAnimals,shuffle($other));
    }
$allAnimals[] = $animal;
}

如果索引不总是第七个呢? - Amal Murali

0
function shuffle_include($a, $include_indexes)    
{    
    $b = array();    
    foreach ($include_indexes as $i => $v)    
        $b[] = $a[$v];    
            
    shuffle($b);    
    
    foreach ($include_indexes as $i => $v)    
        $a[$v] = $b[$i];    
     
    return $a;    
}

例子:

$animals = array (
'giraffe',
'lion',
'hippo',
'dog',
'cat',
'rabbit',
'fly',
'hamster',
'gerbil',
'goldfish');

$new_animals = shuffle_include($animals, array(7,8));

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