// 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');
And so on...
// 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');
And so on...
这是我能做到的最好翻译:
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个操作,或者至少对我来说分成2个操作比较容易。它们在此方法中合并。
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
$combo
为循环变量遍历$combos
$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
进行迭代,作为$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,就会循环,使用 while 循环$array1
从 $array
中添加第一个元素,并将该元素从 $array
中删除(破坏性地)$divided
中存储 $array
和 $array1
$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
)
)
)
getMultiShiftAndDivided
中可以使用一级数组,但它不会给你预期的结果。可能会出现这样的错误://I say probably, but I actually test it ... lol
Warning: array_shift() expects parameter 1 to be array
这可能会让人感到困惑,有人可能认为代码有问题。因此,在第二个方法调用时设置类型为getShiftAndDivided(array $array)
。当包装方法尝试用字符串调用它时,它会崩溃,但是更好的方式是:
Catchable fatal error: Argument 1 passed to Combos::getShiftAndDivided() must be of the type array, string given
希望我的表述让您明白,这是在这种情况下我总是尝试做的事情。长远来看,这会使生活更加方便。两个函数都以相同的格式返回数据,这很方便(不客气)。
概述
因此,它的作用是找到我们输入的所有组合,然后将每个组合分解成移位和分裂的数组。因此,可以推断出我们将所有可能的组合划分为两个数组。因为这几乎就是我所说的。
现在我并不100%确定它是否如此操作,如果您愿意,可以检查一下,它最终会返回约599个元素。祝你好运,我建议只测试$combos->getPossible($input)的结果。如果它有应有的所有组合,那么就具备了必要的功能。我不确定它是否会返回重复项,我认为没有被指定。但我没有真正检查过。
您可以像这样调用主方法:
$input = array('a','b','c','d','e');
print_r((new Combos)->getPossibleAndDivided($input));
附言:我把breaks拼写成brakes,但我可以编写这样的代码,想想看...
<?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
函数接受三个参数。数组,回调函数和一个可选的布尔值,指示是否允许镜像结果。逻辑非常简单:首先决定要放入左侧数组中的元素数量。然后像这样递归调用函数:
[a,c],[b,d,e]
和[c,a],[e,b,d]
是相同的吗? - Nick[a,b],[c,d,e]
和[c,d,e],[a,b]
是相同的吗? - Ultimater