如何在 PHP 中生成多个数组中的所有项目组合
Posted
技术标签:
【中文标题】如何在 PHP 中生成多个数组中的所有项目组合【英文标题】:How to generate in PHP all combinations of items in multiple arrays 【发布时间】:2012-01-23 22:08:46 【问题描述】:我正在尝试在多个数组中查找所有项目组合。 数组的数量是随机的(可以是 2、3、4、5...)。 每个数组的元素个数也是随机的……
例如,我有 3 个数组:
$arrayA = array('A1','A2','A3');
$arrayB = array('B1','B2','B3');
$arrayC = array('C1','C2');
我想生成一个包含 3 x 3 x 2 = 18 个组合的数组:
A1、B1、C1 A1、B1、C2 A1、B2、C1 A1、B2、C2 A1、B3、C1 A1、B3、C2 A2、B1、C1 A2、B1、C2 ...问题是创建一个具有可变数量源数组的函数...
【问题讨论】:
你总是想要 each 数组中的一个元素? 【参考方案1】:这里是递归解决方案:
function combinations($arrays, $i = 0)
if (!isset($arrays[$i]))
return array();
if ($i == count($arrays) - 1)
return $arrays[$i];
// get combinations from subsequent arrays
$tmp = combinations($arrays, $i + 1);
$result = array();
// concat each array from tmp with each element from $arrays[$i]
foreach ($arrays[$i] as $v)
foreach ($tmp as $t)
$result[] = is_array($t) ?
array_merge(array($v), $t) :
array($v, $t);
return $result;
print_r(
combinations(
array(
array('A1','A2','A3'),
array('B1','B2','B3'),
array('C1','C2')
)
)
);
【讨论】:
如果我想要重复数组的唯一组合,我应该如何更改此功能?例如,如果我有 array('A1','A2','A3'), array('A1','A2','A3'), array('C1','C2') 并且我想要结果“A1,A2,C1”,“A1,A3,C1”等,但没有“A1,A1,C1”?另外(如果我没有要求太多;),“A1”,“A2”,“C1”与“A2”,“A1”,“C1”相同,所以我只想要1个组合? @AlexAngelico - 对于其他有相同问题的人,请参阅 array_unique,php.net/manual/en/function.array-unique.php 保存了我的 [...]【参考方案2】:这是一个笛卡尔积,我只是asked the same question not too long ago。这是algorithm that is posted on the PHP website。
function array_cartesian_product($arrays)
$result = array();
$arrays = array_values($arrays);
$sizeIn = sizeof($arrays);
$size = $sizeIn > 0 ? 1 : 0;
foreach ($arrays as $array)
$size = $size * sizeof($array);
for ($i = 0; $i < $size; $i ++)
$result[$i] = array();
for ($j = 0; $j < $sizeIn; $j ++)
array_push($result[$i], current($arrays[$j]));
for ($j = ($sizeIn -1); $j >= 0; $j --)
if (next($arrays[$j]))
break;
elseif (isset ($arrays[$j]))
reset($arrays[$j]);
return $result;
【讨论】:
PHP 网站的链接显然与此功能无关。你能举一个调用它的例子吗? 这个函数处理同一个数组的时间是Lolo函数的2.5倍。【参考方案3】:此代码除了简单之外,还获取多个数组的所有组合并保留键。
function get_combinations($arrays)
$result = array(array());
foreach ($arrays as $property => $property_values)
$tmp = array();
foreach ($result as $result_item)
foreach ($property_values as $property_key => $property_value)
$tmp[] = $result_item + array($property_key => $property_value);
$result = $tmp;
return $result;
示例:
Array
(
Array
(
'1' => 'White',
'2' => 'Green',
'3' => 'Blue'
),
Array
(
'4' =>' Small',
'5' => 'Big'
)
)
将返回:
Array
(
[0] => Array
(
[1] => White
[4] => Small
)
[1] => Array
(
[1] => White
[5] => Big
)
[2] => Array
(
[2] => Green
[4] => Small
)
[3] => Array
(
[2] => Green
[5] => Big
)
[4] => Array
(
[3] => Blue
[4] => Small
)
[5] => Array
(
[3] => Blue
[5] => Big
)
)
【讨论】:
不知道为什么其他人对此表示反对。这个解决方案对我来说非常有效,并且像我想要的那样保留了数组键。 +1 这个解决方案对我来说非常有效,我不得不稍微调整一下以组合多个关联数组。【参考方案4】:我喜欢这个解决方案: https://***.com/a/33259643/3163536 但要回答实际问题(假设每个组合的元素数量应等于传入数组的数量),应修改函数:
function getCombinations(...$arrays)
$result = [[]];
foreach ($arrays as $property => $property_values)
$tmp = [];
foreach ($result as $result_item)
foreach ($property_values as $property_value)
$tmp[] = array_merge($result_item, [$property => $property_value]);
$result = $tmp;
return $result;
用法:
$arrayA = array('A1','A2','A3');
$arrayB = array('B1','B2','B3');
$arrayC = array('C1','C2');
print_r(getCombinations($arrayA, $arrayB, $arrayC));
结果:
Array
(
[0] => Array
(
[0] => A1
[1] => B1
[2] => C1
)
[1] => Array
(
[0] => A1
[1] => B1
[2] => C2
)
[2] => Array
(
[0] => A1
[1] => B2
[2] => C1
)
[3] => Array
(
[0] => A1
[1] => B2
[2] => C2
)
[4] => Array
(
[0] => A1
[1] => B3
[2] => C1
)
[5] => Array
(
[0] => A1
[1] => B3
[2] => C2
)
[6] => Array
(
[0] => A2
[1] => B1
[2] => C1
)
[7] => Array
(
[0] => A2
[1] => B1
[2] => C2
)
[8] => Array
(
[0] => A2
[1] => B2
[2] => C1
)
[9] => Array
(
[0] => A2
[1] => B2
[2] => C2
)
[10] => Array
(
[0] => A2
[1] => B3
[2] => C1
)
[11] => Array
(
[0] => A2
[1] => B3
[2] => C2
)
[12] => Array
(
[0] => A3
[1] => B1
[2] => C1
)
[13] => Array
(
[0] => A3
[1] => B1
[2] => C2
)
[14] => Array
(
[0] => A3
[1] => B2
[2] => C1
)
[15] => Array
(
[0] => A3
[1] => B2
[2] => C2
)
[16] => Array
(
[0] => A3
[1] => B3
[2] => C1
)
[17] => Array
(
[0] => A3
[1] => B3
[2] => C2
)
)
【讨论】:
太棒了,似乎是一个更准确的解决方案 - 我与 Luna 的回答有些不一致【参考方案5】:我知道这个问题很老了,但我今天遇到了同样的问题并决定尝试新的生成器:
function generateCombinations(array $array)
foreach (array_pop($array) as $value)
if (count($array))
foreach (generateCombinations($array) as $combination)
yield array_merge([$value], $combination);
;
else
yield [$value];
foreach (generateCombinations(['a' => ['A'], 'b' => ['B'], 'c' => ['C', 'D'], 'd' => ['E', 'F', 'G']]) as $c)
var_dump($c);
结果:
array(4)
[0]=>
string(1) "E"
[1]=>
string(1) "C"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
array(4)
[0]=>
string(1) "E"
[1]=>
string(1) "D"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
array(4)
[0]=>
string(1) "F"
[1]=>
string(1) "C"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
array(4)
[0]=>
string(1) "F"
[1]=>
string(1) "D"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
array(4)
[0]=>
string(1) "G"
[1]=>
string(1) "C"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
array(4)
[0]=>
string(1) "G"
[1]=>
string(1) "D"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
【讨论】:
当顺序无关紧要时,它是一个组合。当顺序很重要时,它就是一个排列。在这种情况下,它是 Permutation 而不是 Combination。 这是最简单的解决方案 有没有办法做到这一点?array(4) 'd' => string(1) "E" 'c' => string(1) "C" 'b' => string(1) "B" 'a' => string(1) "A"
我的问题的解决方法:创建这样的数组['a' => [['key' => 'a', 'value' => 'A'], ['key' => 'a', 'value' => 'n']]]
以防万一有人希望第一个数组的项目出现在第 2 行使用 array_shift
而不是 array_pop
。【参考方案6】:
还有一个想法:
$ar = [
'a' => [1,2,3],
'b' => [4,5,6],
'c' => [7,8,9]
];
$counts = array_map("count", $ar);
$total = array_product($counts);
$res = [];
$combinations = [];
$curCombs = $total;
foreach ($ar as $field => $vals)
$curCombs = $curCombs / $counts[$field];
$combinations[$field] = $curCombs;
for ($i = 0; $i < $total; $i++)
foreach ($ar as $field => $vals)
$res[$i][$field] = $vals[($i / $combinations[$field]) % $counts[$field]];
var_dump($res);
【讨论】:
【参考方案7】:这是一个从一组数字生成唯一组合的代码。
如果您有一个数字列表,例如 1,3,4,7,12
,您可以生成一组 X
数字,它们都是唯一的,没有重复。
第一个函数在PHP 7.4
或更高版本中工作,第二个函数使用键来存储值。根据基准,两者都运行良好。
function get_combos74($map, $size, &$generated = [], $loop = 1, $i = 0, $prefix = [])
if ($loop == 1)
sort($map);
for (; $i < count($map); $i++)
if ($loop < $size)
get_combos74($map, $size, $generated, $loop + 1, $i + 1, [...$prefix, $map[$i]]);
else
$generated[] = [...$prefix, $map[$i]];
return $generated;
function get_combosSTR($map, $size, &$generated = [], $loop = 1, $i = 0, $prefix = '')
if ($loop == 1)
sort($map);
for (; $i < count($map); $i++)
if ($loop < $size)
get_combosSTR($map, $size, $generated, $loop + 1, $i + 1, "$prefix$map[$i]:");
else
$generated["$prefix$map[$i]"] = 0;
return $generated;
【讨论】:
【参考方案8】:我改进了@mr1031011 给出的解决方案,允许选择性地保留密钥
/**
* Generate an array of permutations from a given array[array[options]]
* e.g.
* ['foo' => [null, 'a'], 'bar' => ['b']]
*
* will generate:
* [null, 'b']
* ['a', 'b']
*
* or with preserveKeys = true:
* ['foo' => null,'bar' => 'b']
* ['foo' => 'a','bar' => 'b']
*
* @param array $array
* @param bool $preserveKeys
*
* @return Generator
*/
function generatePermutations(array $array, bool $preserveKeys = false): Generator
if ($preserveKeys)
end($array);
$key = key($array);
foreach (array_pop($array) as $value)
$item = isset($key) ? [$key => $value] : [$value];
if (!count($array))
yield $item;
continue;
foreach (generatePermutations($array, $preserveKeys) as $combination)
yield array_merge($item, $combination);
【讨论】:
【参考方案9】:使用生成器和数组解包的简单解决方案:
function combinations(array $arrays): iterable
if ($arrays === [])
yield [];
return;
$head = array_shift($arrays);
foreach ($head as $elem)
foreach (self::combinations($arrays) as $combination)
yield [$elem, ...$combination];
在这里试试:https://3v4l.org/Y8clG
结果:
Array
(
[0] => Array
(
[0] => A1
[1] => B1
[2] => C1
)
[1] => Array
(
[0] => A1
[1] => B1
[2] => C2
)
[2] => Array
(
[0] => A1
[1] => B2
[2] => C1
)
[3] => Array
(
[0] => A1
[1] => B2
[2] => C2
)
[4] => Array
(
[0] => A1
[1] => B3
[2] => C1
)
[5] => Array
(
[0] => A1
[1] => B3
[2] => C2
)
[6] => Array
(
[0] => A2
[1] => B1
[2] => C1
)
[7] => Array
(
[0] => A2
[1] => B1
[2] => C2
)
[8] => Array
(
[0] => A2
[1] => B2
[2] => C1
)
[9] => Array
(
[0] => A2
[1] => B2
[2] => C2
)
[10] => Array
(
[0] => A2
[1] => B3
[2] => C1
)
[11] => Array
(
[0] => A2
[1] => B3
[2] => C2
)
[12] => Array
(
[0] => A3
[1] => B1
[2] => C1
)
[13] => Array
(
[0] => A3
[1] => B1
[2] => C2
)
[14] => Array
(
[0] => A3
[1] => B2
[2] => C1
)
[15] => Array
(
[0] => A3
[1] => B2
[2] => C2
)
[16] => Array
(
[0] => A3
[1] => B3
[2] => C1
)
[17] => Array
(
[0] => A3
[1] => B3
[2] => C2
)
)
【讨论】:
【参考方案10】:我从 O'Relly (https://www.oreilly.com/library/view/php-cookbook/1565926811/ch04s25.html) 那里得到这个,
function pc_array_power_set($array)
// initialize by adding the empty set
$results = array(array( ));
foreach ($array as $element)
foreach ($results as $combination)
array_push($results, array_merge(array($element), $combination));
return $results;
然后调用它,
$set = array('A', 'B', 'C');
$power_set = pc_array_power_set($set);
这应该可以解决问题!
【讨论】:
以上是关于如何在 PHP 中生成多个数组中的所有项目组合的主要内容,如果未能解决你的问题,请参考以下文章