常見規格排列組合問題

李照耀發表於2024-11-20

在做商城系統中最常見的就是規格,簡寫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);

發現結果是一樣的,大功告成!

上面遞迴的思路是遞迴發現自己是不是最後一個,不是的話就再遞迴,最終找到最後一個,形成組合,然後再與倒數第二個組合,再與倒數第三個組合。

這裡我的思路是,先拿出第一個自然形成結果,再拿出第二個進行組合,然後拿出第三個跟現有結果進行組合,直到拿出最後一個規格進行組合最終形成結果賦值。

方法沒有優劣,只有是不是適合以及你自己能不能接受,能不能維護。