我需要以随机顺序对数组进行洗牌,而两个并发值不相同。
$array("red", "green", "blue", "red", "green", "blue", "red", "green", "blue", etc);
输出应该是包含随机顺序中所有值的任何有效数组,而没有两个值彼此相邻。
因此,有效选项:
$array("blue", "red", "blue", "green", "red", "blue", "green");
我在考虑计算" red"," blue"和"绿色"
array_count_values($array);
array_count_values($array)["red"];
array_count_values($array)["green"];
array_count_values($array)["blue"];
然后可能使用每个字符串的可用数量创建一个数组。
答案 0 :(得分:1)
这是我的第一个方法,旨在最小化循环以实现所需的结果。
代码:(Demo with 3 different arrays and echo & var_export throughout the process)
// add $array here
$length=sizeof($array);
shuffle($array);
$valcounts=array_count_values($array);
function consec_check($array){
$loops=sizeof($array)-1; // last element will not have right side element for comparison
for($i=0; $i<$loops; ++$i){
if($array[$i]==$array[$i+1]){
return false; // consecutive equal values = invalid
}
}
return true;
}
if(max($valcounts)<=ceil($length/2)){ // if logically possible to fix
while(!consec_check($array)){ // while any two equal elements are consecutive
foreach(array_diff($valcounts,[1]) as $color=>$count){ // only bother with elements that occur more than once
$colorkeys=array_keys($array,$color); // color group keys
for($i=0; $i<$count; ++$i){
if($i>0 && $prevk+1==$colorkeys[$i]){ // identify consecutives elements with same color
if($colorkeys[0]!=0){ // safe to shift {$colorkeys[$i]} to first position
array_unshift($array,array_splice($array,$colorkeys[$i],1)[0]);
}elseif(end($colorkeys)!=$length-1){ // safe to push {$colorkeys[$i]} to the last position
array_push($array,array_splice($array,$colorkeys[$i],1)[0]);
}else{ // no easy option, find a safe location inside array (more frequently used as array length increases)
for($j=0; $j<$count; ++$j){
if($j>0 && $colorkeys[$j]-$prevj>3){ // if 3 off-colors between two elements array_splice($array,$prevj+2,0,array_splice($array,$colorkeys[$i],1));
break;
}
$prevj=$colorkeys[$j];
}
}
$colorkeys=array_keys($array,$color); // update color keys array for continued processing
}
$prevk=$colorkeys[$i];
}
}
}
var_export($array); // valid
}else{
echo "\n\n<a href=\"https://www.youtube.com/watch?v=XAYhNHhxN0A\">Array cannot be made valid.</a>";
}
这是我使用正则表达式模式的第二种方法。
代码:(Demo with 3 different arrays and echo & var_export throughout the process)
shuffle($array);
$string=implode(' ',$array);
$start_length=strlen($string);
foreach(array_unique($array) as $v){
$pullcount=$pushcount1=$pushcount2=0;
$string=preg_replace("/$v (?=$v)/","",$string,-1,$pullcount); // remove the first value of each conflicting pair
$string=preg_replace("/ \K(?<!$v )(?!$v)|^(?!$v)/","$v ",$string,$pullcount,$pushcount1); // foreach removal, re-insert value(s) where valid
if($pullcount<=$pushcount1){
$string=preg_replace("/$(?<!$v)/"," $v",$string,$pullcount-$pushcount1,$pushcount2);
}
if($pullcount!=$pushcount1+$pushcount2){
echo "failure while replacing $v $pullcount & ",$pushcount1+$pushcount2,"\n";
break;
}else{
echo "successfully replaced $pullcount conflicts for $v\n";
}
}
if($start_length==strlen($string)){
$array=explode(" ",$string);
var_export($array);
}else{
echo "\n<a href=\"https://www.youtube.com/watch?v=XAYhNHhxN0A\">Array cannot be made valid.</a>";
}
我的第二种方法在简洁方面胜出,但在值包含空格或值是另一个值的子字符串的其他情况下,它可能不值得信赖。
这两种方法都避免了无限循环的可能性,并指出数组是否无法生效。