如何遍历一个数组,将其拆分为两个数组,并为每种可能的组合运行一个函数?顺序没关系。
// Original array
$array = array('a','b','c','d','e');
// Result 1
array('a');
array('b','c','d','e');
// Result 2
array('a', 'b');
array('c','d','e');
// Result 3
array('a', 'c');
array('b','d','e');
依此类推...
答案 0 :(得分:2)
这是我能做的最好的事情
class Combos
{
/**
* getPossible then getDivide
*
* @param array $input
* @return array
*/
public function getPossibleAndDivided( array $input )
{
return $this->getMultiShiftAndDivided( $this->getPossible( $input ) );
}
/**
* return all possible combinations of input
*
* @param array $input
* @return array
*/
public function getPossible( array $inputs )
{
$result = [];
if ( count( $inputs ) <= 1 ) {
$result = $inputs;
} else {
$result = array();
foreach($inputs as $input){
//make it an array
$first = [ $input ];
//get all inputs not in first
$remaining = array_diff( $inputs, $first );
//send the remaining stuff but to ourself
$combos = $this->getPossible( $remaining );//recursive
//iterate over the above results (from ourself)
foreach( $combos as $combo ) {
$last = $combo;
//convert to array if it's not
if( !is_array( $last ) ) $last = [ $last ];
//merge them
$result[] = array_merge( $first, $last );
}
}
}
return $result;
}
/**
* shift and divide a multi level array
*
* @param array $array
* @return array
*/
public function getMultiShiftAndDivided( array $mArray )
{
$divided = [];
foreach ( $mArray as $array ) {
$divided = array_merge($divided, $this->getShiftAndDivided( $array ));
}
return $divided;
}
/**
* shift and divide a single level array
*
* @param array $array
* @return array
*/
public function getShiftAndDivided( array $array )
{
$divided = [];
$array1 = [];
while( count( $array ) ){
$array1[] = array_shift( $array );
$divided[] = [ $array, $array1 ];
}
return $divided;
}
}
工作方式
顶层,我不想深入了解所有细节。这基本上是一个两步过程,或者至少更容易以这种方式解决。我在一个类中构建它,以使所有内容保持整洁。它还可以提供更好的单元测试和可重用性。
这需要2次操作,或者至少对我而言,它更容易以2次而不是1次进行。
public function getPossibleAndDivided( array $input )
{
return $this->getMultiShiftAndDivided( $this->getPossible( $input ) );
}
这是我使它成为一门课的主要原因,以使所有内容都很好地打包在一起。我将其称为包装器方法。
第一步
$ Combos-> getPossible(array $ input)
$inputs
。
$inputs
迭代为$input
$input
包装为名为$first
的数组
$inputs
$inputs
中$remaining
中的其余元素
$this->getPossible($remaining)
(请参阅#1)并返回为$combos
$combos
的foreach迭代为$combo
$combo
被分配给$last
并变成一个数组(如果不是)
$first
和$last
的合并
$result
中的所有内容。这基本上旋转了数组的所有组合,并以如下形式返回它们:
Array
(
[0] => Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
)
[1] => Array
(
[0] => a
[1] => b
[2] => c
[3] => e
[4] => d
)
[2] => Array
(
[0] => a
[1] => b
[2] => d
[3] => c
[4] => e
)
...
[117] => Array
(
[0] => e
[1] => d
[2] => b
[3] => c
[4] => a
)
[118] => Array
(
[0] => e
[1] => d
[2] => c
[3] => a
[4] => b
)
[119] => Array
(
[0] => e
[1] => d
[2] => c
[3] => b
[4] => a
)
)
是的,它返回119
个结果,不,我不会全部包含它们。
第二步
别忘了上面的输出是一个多维数组(这在下面很重要)。
$ Combos-> getMultiShiftAndDivided(array $ mArray)
该方法旨在用于多维数组(因此得名)。我们从“步骤1”中获得此信息。它基本上是 $ Combos-> getShiftAndDivided($ array)
的包装$mArray
的foreach迭代为$array
$this->getShiftAndDivided($array)
返回并与$divided
合并。
输出示例:
$input = array(array('a','b','c','d','e'));
print_r($combos->getMultiShiftAndDivided($input));
Array
(
[0] => Array
(
[0] => Array
(
[0] => b
[1] => c
[2] => d
[3] => e
)
[1] => Array
(
[0] => a
)
)
....
[4] => Array
(
[0] => Array
(
)
[1] => Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
)
)
)
$ Combos-> getShiftAndDivided(array $ array)
此方法旨在用于单级数组。
$array
的计数大于0,就循环,然后循环$array1
从添加的$array
中获取第一个元素,并从$array
中删除该元素(破坏性地)$array
中存储$array1
和$divided
$array
中没有其他项目时,我们返回结果输出示例:
$input = array('a','b','c','d','e');
print_r($combos->getShiftAndDivided($input));
Array
(
[0] => Array
(
[0] => Array
(
[0] => b
[1] => c
[2] => d
[3] => e
)
[1] => Array
(
[0] => a
)
)
....
[4] => Array
(
[0] => Array
(
)
[1] => Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
)
)
)
基本上,这会将单个数组的元素转换为两个结果数组,并在每次转换时记录其状态。我将其设置为2个函数,以便可以更轻松地对其进行测试和重新使用。
另一个问题是很难检查多维数组。我知道该怎么做,但是我不喜欢它,因为它有点丑陋,还有更好的方法。我之所以这样说,是因为可以在getMultiShiftAndDivided
中使用一个一级数组,并且它不会给您您所期望的。可能会收到如下错误:
//I say probably, but I actually test it ... lol
Warning: array_shift() expects parameter 1 to be array
这可能会引起混淆,但人们可能会认为代码是错误的。因此,通过将第二种方法调用的类型设置为getShiftAndDivided( array $array )
。当wrapping方法尝试用字符串调用它时,它会被炸掉,但是会更好:
Catchable fatal error: Argument 1 passed to Combos::getShiftAndDivided() must be of the type array, string given
希望这是有道理的,在这种情况下,我总是尝试这样做。从长远来看,这只会使生活更轻松。这两个函数都以相同的格式返回数据,这很方便(不客气)。
摘要
因此,这样做的总和是找到我们输入的所有组合,然后将其取用并将每个输入分解为移位和分割的数组。出于这个理由,我们无论如何都将所有可能的组合分成2个数组。因为那几乎就是我所说的。
现在我不是100%做到这一点,您可以根据需要检查它们,最后返回599个元素。因此,我很幸运,我建议您仅测试 $ combos-> getPossible($ input)的结果。如果它具有应有的所有组合,那么它将具有所需的全部。我不确定它是否返回重复项,我认为没有指定。但是我并没有真正检查它。
您可以这样调用main方法:
$input = array('a','b','c','d','e');
print_r((new Combos)->getPossibleAndDivided($input));
P.S。我把刹车当刹车,但我可以这样写代码,图...
答案 1 :(得分:2)
这是我的看法:
<?php
$ar = ['a','b','c','d','e'];
function permuteThrough($ar, $callback, $allowMirroredResults = true, $mode = 'entry', $desiredLeftCount = null, $left = [], $right = [])
{
switch($mode)
{
case 'entry':
// Logic:
// Determine how many elements we're gonna put into left array
$len = $allowMirroredResults ? count($ar) : floor(count($ar)/2);
for($i=0; $i <= $len; $i++)
{
call_user_func(__FUNCTION__, $ar, $callback, $allowMirroredResults, 'permute',$i);
}
break;
case 'permute':
// We have nothing left to sort, let's tell our callback
if( count($ar) === 0 )
{
$callback($left,$right);
break;
}
if( count($left) < $desiredLeftCount )
{
// Note: PHP assigns arrays as clones (unlike objects)
$ar1 = $ar;
$left1 = $left;
$left1[] = current(array_splice($ar1,0,1));
call_user_func(__FUNCTION__, $ar1, $callback, $allowMirroredResults, 'permute', $desiredLeftCount, $left1, $right);
}
// This check is needed so we don't generate permutations which don't fulfill the desired left count
$originalLength = count($ar) + count($left)+count($right);
if( count($right) < $originalLength - $desiredLeftCount )
{
$ar2 = $ar;
$right1 = $right;
$right1[] = current(array_splice($ar2,0,1));
call_user_func(__FUNCTION__, $ar2, $callback, $allowMirroredResults, 'permute', $desiredLeftCount, $left, $right1);
}
break;
}
}
function printArrays($a,$b)
{
echo '['.implode(',',$a).'],['.implode(',',$b)."]\n";
}
permuteThrough($ar, 'printArrays', true); // allows mirrored results
/*
[],[a,b,c,d,e]
[a],[b,c,d,e]
[b],[a,c,d,e]
[c],[a,b,d,e]
[d],[a,b,c,e]
[e],[a,b,c,d]
[a,b],[c,d,e]
[a,c],[b,d,e]
[a,d],[b,c,e]
[a,e],[b,c,d]
[b,c],[a,d,e]
[b,d],[a,c,e]
[b,e],[a,c,d]
[c,d],[a,b,e]
[c,e],[a,b,d]
[d,e],[a,b,c]
[a,b,c],[d,e]
[a,b,d],[c,e]
[a,b,e],[c,d]
[a,c,d],[b,e]
[a,c,e],[b,d]
[a,d,e],[b,c]
[b,c,d],[a,e]
[b,c,e],[a,d]
[b,d,e],[a,c]
[c,d,e],[a,b]
[a,b,c,d],[e]
[a,b,c,e],[d]
[a,b,d,e],[c]
[a,c,d,e],[b]
[b,c,d,e],[a]
[a,b,c,d,e],[]
*/
echo "==============\n"; // output separator
permuteThrough($ar, 'printArrays', false); // doesn't allow mirrored results
/*
[],[a,b,c,d,e]
[a],[b,c,d,e]
[b],[a,c,d,e]
[c],[a,b,d,e]
[d],[a,b,c,e]
[e],[a,b,c,d]
[a,b],[c,d,e]
[a,c],[b,d,e]
[a,d],[b,c,e]
[a,e],[b,c,d]
[b,c],[a,d,e]
[b,d],[a,c,e]
[b,e],[a,c,d]
[c,d],[a,b,e]
[c,e],[a,b,d]
[d,e],[a,b,c]
*/
我的permuteThrough
函数带有三个参数。
数组,回调和一个可选的布尔值,指示是否要允许镜像结果。
逻辑很简单:
首先决定要在左侧数组中放入多少个元素。
然后像这样递归调用该函数:
我们的工作数组将其余元素进行排序。
移开一个元素并将其放入左侧数组。结果将发送到另一层递归。
移开元素并将其放入正确的数组。结果将发送到另一层递归。
如果没有剩余要偏移的元素,请使用产生的左右数组调用回调。
最后,确保在开始时没有超过由for循环确定的所需左数组元素的大小,并确保正确的大小不会变得太大以至于无法满足所需的左大小。通常,这将由两个单独的功能完成。一个决定应将多少个元素放入左数组。和一个用于递归。但是,相反,我将递归函数的另一个参数扔掉了,以消除对单独函数的需要,这样就可以由同一个递归函数来全部处理。