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 个数组的笛卡尔积的主要内容,如果未能解决你的问题,请参考以下文章