N 个数组的笛卡尔积

Posted

技术标签:

【中文标题】N 个数组的笛卡尔积【英文标题】:Cartesian Product of N arrays 【发布时间】:2011-01-31 18:37:43 【问题描述】:

我有一个类似于这个例子的 php 数组:

$array[0][0] = 'apples';
$array[0][1] = 'pears';
$array[0][2] = 'oranges';

$array[1][0] = 'steve';
$array[1][1] = 'bob';

我希望能够从这个表格中生成一个包含这些所有可能组合的表格,但不重复任何组合(无论它们的位置如何),所以例如这将输出

Array 0            Array 1
apples             steve
apples             bob
pears              steve
pears              bob

但我希望它能够处理尽可能多的不同数组。

【问题讨论】:

会有数组2、数组3、数组N吗?还是只有两个数组? 您好,很抱歉没有更清楚地说明,可能有数组 2、数组 3 到数组 n。谢谢。 你需要的是在 SQL 中轻松完成的交叉连接,但在 PHP 中需要一些思考 碰巧这是来自一个 mysql 数据库,所有选项(苹果、梨、橙子、史蒂夫、鲍勃等)都在一个表中,一个指向另一个表的键,定义了它们属于哪个组在(水果,人等),有什么想法可以在 mysql 中工作吗? 【参考方案1】:

这被称为“笛卡尔积”,数组http://php.net/manual/en/ref.array.php 上的 php 手册页显示了一些实现(在 cmets 中)。

还有一个:

function array_cartesian() 
    $_ = func_get_args();
    if(count($_) == 0)
        return array(array());
    $a = array_shift($_);
    $c = call_user_func_array(__FUNCTION__, $_);
    $r = array();
    foreach($a as $v)
        foreach($c as $p)
            $r[] = array_merge(array($v), $p);
    return $r;


$cross = array_cartesian(
    array('apples', 'pears',  'oranges'),
    array('steve', 'bob')
);

print_r($cross);

【讨论】:

如果这个函数存在于一个类中,你可能想像这样改变 user_func 调用:$c = call_user_func_array(array($this,__FUNCTION__), $_);。此外,如果输入数组的大小不相等,它会发出警告(不是数组)。 这是一个很好的解决方案,但第一次短路的结果是真实的,这对我来说没有意义。试试this one。【参考方案2】:

您正在寻找数组的笛卡尔积,php 数组网站上有一个示例:http://php.net/manual/en/ref.array.php

【讨论】:

【参考方案3】:

Syom 复制了 http://www.php.net/manual/en/ref.array.php#54979,但我将其改编为关联版本:

function array_cartesian($arrays) 
  $result = array();
  $keys = array_keys($arrays);
  $reverse_keys = array_reverse($keys);
  $size = intval(count($arrays) > 0);
  foreach ($arrays as $array) 
    $size *= count($array);
  
  for ($i = 0; $i < $size; $i ++) 
    $result[$i] = array();
    foreach ($keys as $j) 
      $result[$i][$j] = current($arrays[$j]);
    
    foreach ($reverse_keys as $j) 
      if (next($arrays[$j])) 
        break;
      
      elseif (isset ($arrays[$j])) 
        reset($arrays[$j]);
      
    
  
  return $result;

【讨论】:

【参考方案4】:

我也需要这样做,我尝试了此处发布的先前解决方案,但无法使它们发挥作用。我从这个聪明的家伙http://www.php.net/manual/en/ref.array.php#54979 那里得到了一个样本。然而,他的样本没有处理不重复组合的概念。所以我包括了那部分。这是我的修改版,希望对你有帮助:

$data = array(
        array('apples', 'pears',  'oranges'),
        array('steve', 'bob')
    );

    $res_matrix = $this->array_cartesian_product( $data );

    foreach ( $res_matrix as $res_array )
    
        foreach ( $res_array as $res )
        
            echo $res . " - ";
        
        echo "<br/>";
    


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 );
    $res_index = 0;
    for ( $i = 0; $i < $size; $i++ )
    
        $is_duplicate = false;
        $curr_values  = array();
        for ( $j = 0; $j < $sizeIn; $j++ )
        
            $curr = current( $arrays[$j] );
            if ( !in_array( $curr, $curr_values ) )
            
                array_push( $curr_values , $curr ); 
            
            else
            
                $is_duplicate = true;
                break;
            
        
        if ( !$is_duplicate )
        
            $result[ $res_index ] = $curr_values;
            $res_index++;
        
        for ( $j = ( $sizeIn -1 ); $j >= 0; $j-- )
        
            $next = next( $arrays[ $j ] );
            if ( $next )
            
                break;
            
            elseif ( isset ( $arrays[ $j ] ) )
            
                reset( $arrays[ $j ] );
            
        
    
    return $result;

结果会是这样的: 苹果 - 史蒂夫 苹果 - 鲍勃 梨 - 史蒂夫 梨 - 鲍勃 橘子 - 史蒂夫 橘子 - 鲍勃

如果你的数据数组是这样的:

  $data = array(
        array('Amazing', 'Wonderful'),
        array('benefit', 'offer', 'reward'),
        array('Amazing', 'Wonderful')
    );

然后它将打印如下内容:

惊人 - 好处 - 精彩 惊人 - 提供 - 精彩 惊人 - 奖励 - 精彩 精彩 - 受益 - 惊人 很棒 - 提供 - 很棒 精彩 - 奖励 - 惊人

【讨论】:

【参考方案5】:
foreach($parentArray as $value) 
    foreach($subArray as $value2) 
        $comboArray[] = array($value, $value2); 
    

不要评判我..

【讨论】:

【参考方案6】:

我认为这很有效——虽然在写完之后我意识到它与其他人所放的非常相似,但它确实为您提供了所需格式的数组。很抱歉变量命名不佳。

$output = array();
combinations($array, $output);
print_r($output);

function combinations ($array, & $output, $index = 0, $p = array()) 
    foreach ( $array[$index] as $i => $name ) 
        $copy = $p;
        $copy[] = $name;
        $subIndex = $index + 1;
        if (isset( $array[$subIndex])) 
            combinations ($array, $output, $subIndex, $copy);
         else 
            foreach ($copy as $index => $name) 
                if ( !isset($output[$index])) 
                    $output[$index] = array();   
                
                $output[$index][] = $name;   
            
        
    

【讨论】:

【参考方案7】:

@user187291

我修改为

function array_cartesian() 
    $_ = func_get_args();
    if (count($_) == 0)
        return array();
    $a = array_shift($_);
    if (count($_) == 0)
        $c = array(array());
    else
        $c = call_user_func_array(__FUNCTION__, $_);
    $r = array();
    foreach($a as $v)
        foreach($c as $p)
            $r[] = array_merge(array($v), $p);
    return $r;

因此,当您传递 0 个参数时,它会返回最重要的空数组(与没有组合的结果相同)。

只注意到这一点,因为我正在使用它

$combos = call_user_func_array('array_cartesian', $array_of_arrays);

【讨论】:

【参考方案8】:
function array_comb($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;

【讨论】:

【参考方案9】:

我必须从产品选项中进行组合。此解决方案使用递归并使用 2D 数组:

function options_combinations($options) 
    $result = array();
    if (count($options) <= 1) 
        $option = array_shift($options);
        foreach ($option as $value) 
            $result[] = array($value);
        
     else 
        $option = array_shift($options);
        $next_option = options_combinations($options);
        foreach ($next_option as $next_value) 
            foreach ($option as $value) 
                $result[] = array_merge($next_value, array($value));
            
        
    
    return $result;


$options = [[1,2],[3,4,5],[6,7,8,9]];
$c = options_combinations($options);
foreach ($c as $combination) 
    echo implode(' ', $combination)."\n";

【讨论】:

【参考方案10】:

基于原生Python函数itertools.product的优雅实现

function direct_product(array ...$arrays)

    $result = [[]];
    foreach ($arrays as $array) 
        $tmp = [];
        foreach ($result as $x) 
            foreach ($array as $y) 
                $tmp[] = array_merge($x, [$y]);
            
        
        $result = $tmp;
    
    return $result;

【讨论】:

以上是关于N 个数组的笛卡尔积的主要内容,如果未能解决你的问题,请参考以下文章

什么是笛卡尔积?

在笛卡尔积中,元数与元组个数是相同的意思吗

笛卡尔积请具体解释一下.

数据库笛卡尔积

使用 JPA 获取集合时避免 N+1 和笛卡尔积问题的标准方法是啥

简述啥是关系、元组、属性、域、主码?