重新排列数组,以便PHP中没有相邻的值相同

时间:2017-04-04 23:26:51

标签: php arrays

我需要以随机顺序对数组进行洗牌,而两个并发值不相同。

$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"];

然后可能使用每个字符串的可用数量创建一个数组。

1 个答案:

答案 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>";
}

我的第二种方法在简洁方面胜出,但在值包含空格或值是另一个值的子字符串的其他情况下,它可能不值得信赖。

这两种方法都避免了无限循环的可能性,并指出数组是否无法生效。