【问题标题】:How can I generate all unique combinations of multiple array's with optionals?如何生成具有选项的多个数组的所有唯一组合?
【发布时间】:2016-10-04 20:48:48
【问题描述】:

我想组合多个数组中的所有值以创建独特的组合。有一个问题:一些数组可以是可选的。

例如:我正在配置一台 PC,我可以选择:

  • 5种机箱(包括风扇、电源和主板)
  • 5 种类型的磁盘(可选)
  • 5 种内存(可选)
  • 5 种显卡(可选)

如您所见,结果可以是任何组合:

  1. 机箱类型 1、磁盘类型 3、内存类型 5、显卡类型 1
  2. 机箱类型 1、磁盘类型 3、内存类型 5、显卡类型 2
  3. 机箱类型 1、磁盘类型 3、内存类型 5、显卡类型 3
  4. 机箱类型 1,无磁盘,内存类型 5,显卡类型 2
  5. 机箱类型 1,无磁盘,内存类型 5,无显卡
  6. 机箱类型 1,无磁盘,无内存,无显卡

为了确定一系列产品是否是可选的,'optional' => [0|1] 部分已包含在数组中:-)

以下数组是“生产”中使用的数组的摘录:

array(
    array('optional' => 0, 0, 1),
    array('optional' => 0, 3, 4),
    array('optional' => 0, 6, 7, 8),
    array('optional' => 1, 6, 7, 8, 2),
    array('optional' => 1, 6, 7, 8, 5, 9),
    array('optional' => 1, 6, 7, 8, 10, 11, 12)
)

输出应该是这样的:

0, 3, 6
0, 3, 7
0, 3, 8
0, 4, 6
0, 4, 7
0, 4, 8
1, 3, 6
1, 3, 7
1, 3, 8
[...]
0, 3, 6, 2  <-- outcome with an optional product from the 4th array
0, 3, 7, 9  <-- outcome with an optional product from the 5th array
0, 3, 8, 12 <-- outcome with an optional product from the 6th array

如您所见,上述数组合并为一个数组。一些子数组是强制性的,其中 optional = 0,或可选,其中 optional = 1。

在有可选数组之前,我使用了以下函数:

<?PHP
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($ArrCombinateMe) as $combination ){
    // Some code here
}

该函数运行良好,所以我想使用类似的东西,准确地说,我不想丢失生成器函数,因为它对内存非常友好(我之前的函数会在返回可用输出之前组合所有内容,它只能返回320 万个组合,4GB 内存。这个函数在测试中已经超过了 320 万个千倍)。

目前我还想包含可选数组,以便生成这些数组 :-)

请注意:我喜欢速度,但对于这个功能来说,这并不重要,因为它会作为后台作业运行而无需任何用户交互。

我希望有人可以帮助我:-)

【问题讨论】:

  • 您想选择包含可选内容吗?因为如果您只想包含可选内容,那么其中就没有可选内容。
  • 没错,可以选择包含可选产品,但您不必这样做。例如:我正在配置一台PC,我有5种机箱(包括风扇、电源和主板)、5种磁盘(可选)、5种内存和5种显卡(可选)的选择)。如您所见,结果可以是任何组合:Chassis type 1, Disk type 3, Memory type 5, videocard type 4. Chassis type 1, no disk, Memory type 5, videocard type 2. Chassis type 1, no disk, Memory type 5, no videocard. 因此数组中的'optional' =&gt; [0|1] 部分:-)
  • 如果 optional 为 0 或 1,那么您只能获得所有可选组合或无,因此您的 [0, 3, 6, 2] 作为第 4 个数组(仅)的选项将永远不会生成,因为您还选择了第 5 个和第6个选项。结果数组中将有 3 个或 6 个元素。您需要一种方法来确定是否选择了选项。
  • 感谢您的回答 :-) 可选产品(内存、视频卡等)未链接,因此其中一个可以是可选的,但另一个也可以是强制性的。可选产品在管理界面中使用“制作可选触发器”进行设置。我们检查哪些产品可以组合,哪些不能;然后我们对产品(即内存)进行分组,并制作兼容的产品组组合。因此,[0, 3, 6, 2] 是可能的。我希望你明白:-)
  • 所以结果应该是 3 到 6 个字段长的所有组合(因为最后 3 个是可选的)对吗?此数据结构是否可以更改或已经实现?

标签: php arrays combinations


【解决方案1】:

似乎答案比理解问题要简单得多。 您不需要更改过程 - 只需在可选数组中包含“null”作为可能值。这意味着该部分不包括在内。

这样会有返回空值的数组,但它们的索引将代表源。下面以数组为例:

$data = array(
    'Chassis'   => array(0, 1, 2),
    'Mainboard' => array(3, 4, 5),
    'PSU'       => array(6, 7, 8),
    'Disk'      => array(null, 9, 10),
    'GFX'       => array(null, 11, 12),
    'Memory'    => array(null, 13, 14, 15)
);

其中一个结果是:[0,3,6,null,11,null]*,这意味着 GFX 已包含在内。

如果您不想使用空值,您可以过滤该结果

array_filter($combination, 'is_int')

'is_int' 参数仅用于正确处理0。如果 0 不是有效的 id,那么您可以跳过它(然后可以使用 0 而不是 null)

*) 实际上最后一个元素因为array_merge() args 顺序跳转到第一个位置

编辑:

这个生成器本身的速度提高了约 35%(相同的内存使用)并且不需要过滤,总体速度提高了一倍:

function generateCombinations(array $array) {
    foreach (array_pop($array) as $id) {
        if (empty($array)) {
            yield isset($id) ? [$id] : [];
            continue;
        }

        foreach (generateCombinations($array) as $combination) {
            if (isset($id)) { $combination[] = $id; }
            yield $combination;
        }
    }
}

【讨论】:

  • 哇好找!我在可选数组中添加了空值,并在generateCombinations() 函数中简单地调整了这一行:yield array_merge([$value], $combination); 变为yield array_filter(array_merge([$value], $combination), 'is_int');。我已经对它进行了小批量测试,效果很好! :-) 谢谢!
  • @Robbert 我已经优化了那个生成器(去掉了慢速函数)——见编辑。
  • 非常感谢! :-) 我已经用一个大数据集验证了这个函数,产生了 1.3 亿个独特的组合,它快了大约 2 分钟(12 分钟对 10 分钟)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-16
相关资源
最近更新 更多