常见规格排列组合问题
在做商城系统中最常见的就是规格,简写pcs。具体来说就是一个商品有多个属性,每个属性有多个规格,这样就形成一些排列组合,做商品库存的时候就要对这些组合进行设定库存和价格。
比如,一台电脑内存有16G,32G和64G的,硬盘有500G和1T的,显卡有集成显卡和独立显卡的,这样的商品在售卖的时候选定不同规格价格不一样,仓库备货的库存当然也不一样。
那么对商城后台而言就要单独设定这些产品的价格,就需要把所有的组合排列出来设定价格。
现在我们已知应该产生这样的排列组合然后设定价格和库存(这里面的价格和库存只是为了程序举个例子 并不是实际市场上卖这个价格)
内存 | 硬盘 | 显卡 | 价格 | 库存 |
16G | 500G | 集成 | 5000 | 100 |
16G | 500G | 独立 | 6000 | 200 |
16G | 1T | 集成 | 7000 | 220 |
16G | 1T | 独立 | 6500 | 120 |
32G | 500G | 集成 | 7000 | 110 |
32G | 500G | 独立 | 7500 | 200 |
32G | 1T | 集成 | 7500 | 300 |
32G | 1T | 独立 | 8000 | 200 |
64G | 500G | 集成 | 7500 | 134 |
64G | 500G | 独立 | 8000 | 347 |
64G | 1T | 集成 | 8500 | 258 |
64G | 1T | 独立 | 9000 | 35 |
这里一共是12种组合,那如何用程序生成这样的组合呢?
我们已知从数据库读取能够拿到的属性变量如下:
<?php $spec_list = [ 'memory' => ['16G', '32G', '64G'], 'storage' => ['500G', '1T'], 'graphics' => ['integrated', 'discrete'], ];
我们想要的肯定是一个数组,里面有12个元素,每个元素中就是这些规格的组合。
类似于这样的结果
[ ['16G', '500G', '集成'], ['16G', '500G', '独立'], ['16G', '1T', '集成'], ['16G', '1T', '独立'], ['32G', '500G', '集成'], ['32G', '500G', '独立'], ['32G', '1T', '集成'], ['32G', '1T', '独立'], ['64G', '500G', '集成'], ['64G', '500G', '独立'], ['64G', '1T', '集成'], ['64G', '1T', '独立'], ]
那么怎么实现呢?你要是写三层循环,进行嵌套实现,那也没问题,就是这个属性的个数你得明确的知道有多少种,这样毕竟不是长久之计,而且属性有可能增加或者减少,那将来后台增加了属性,程序该怎么办呢?
还是得自动的计算出来所有组合,不需要手动去检测有多少种属性去foreach遍历。
这里我偷懒的让vscode里面的插件 fitten code 帮我写了一个
<?php function generateCombinations($attributes) { if (count($attributes) === 0) { return [[]]; } $firstAttribute = array_shift($attributes); $combinationsWithoutFirst = generateCombinations($attributes); $combinations = []; foreach ($firstAttribute as $value) { foreach ($combinationsWithoutFirst as $combination) { $combinations[] = array_merge([$value], $combination); } } return $combinations; }
调用一下看看
<?php $spec_list = [ 'memory' => ['16G', '32G', '64G'], 'storage' => ['500G', '1T'], 'graphics' => ['集成', '独立'], ]; $combinations = generateCombinations($spec_list); var_dump($combinations);
从执行结果来看,是没问题的。
果然是AI改变未来啊,这生成速度比我手动敲的快多了。但是我们也能发现,它用了递归,按道理来说,这种普通的规格属性也不会太多,排列组合几十个已经很多了,再多的话,客户端那边显示就不好看了,而且客户操作起来就不方便,那递归的性能也不会太差。
但是咱们是追求极致的人啊,我再写个循环实现的,避免程序效率太低,看看怎么实现呢?
首先得弄个大循环,在里面一直进行循环,然后还得判断这种组合是否有过,层数如何解决呢?
我想可以先实现二层,再实现多层,逐步增加组合列表,话不多说,看代码
<?php function get_combine_list($spec_list) { if (count($spec_list) <= 1) { return $spec_list; } $result = []; $first_item = array_shift($spec_list); foreach ($first_item as $first_value) { $result[] = [$first_value]; } while ($spec_list) { $tmp_result = []; $second = array_shift($spec_list); foreach ($result as $result_item) { foreach ($second as $second_value) { $tmp_result[] = array_merge($result_item, [$second_value]); } } $result = $tmp_result; } return $result; }
好了,同样的代码调用一下
<?php $spec_list = [ 'memory' => ['16G', '32G', '64G'], 'storage' => ['500G', '1T'], 'graphics' => ['集成', '独立'], ]; $combinations = get_combine_list($spec_list);
发现结果是一样的,大功告成!
上面递归的思路是递归发现自己是不是最后一个,不是的话就再递归,最终找到最后一个,形成组合,然后再与倒数第二个组合,再与倒数第三个组合。
这里我的思路是,先拿出第一个自然形成结果,再拿出第二个进行组合,然后拿出第三个跟现有结果进行组合,直到拿出最后一个规格进行组合最终形成结果赋值。
方法没有优劣,只有是不是适合以及你自己能不能接受,能不能维护。