如何在 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' =&gt; string(1) "E" 'c' =&gt; string(1) "C" 'b' =&gt; string(1) "B" 'a' =&gt; string(1) "A" 我的问题的解决方法:创建这样的数组['a' =&gt; [['key' =&gt; 'a', 'value' =&gt; 'A'], ['key' =&gt; 'a', 'value' =&gt; '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 中生成多个数组中的所有项目组合的主要内容,如果未能解决你的问题,请参考以下文章

PHP算法从单个集合中生成特定大小的所有组合

ruby - 如何从字符串数组中生成可能的字母顺序组合?

如何在 PHP 中生成字符串的所有排列?

在python中生成组合

php当中,如何将数组合并成变量呢?

如何在字典中生成所有可能的组合