SQL 揹包問題

這是一道簡化的揹包問題:有一揹包能容納 50kg 的物品,現有 9 種物品(它們的重量分別是5kg、8sql

kg、20kg、35kg、41kg、2kg、15kg、10kg、9kg),要恰好能裝滿揹包,有多少種物品組合?優化

因爲要用到 SQL 來處理,咱們先把上面的物品的重量的數據存到表中,並給每種物品分配一個編號。物品表 bag 的數據以下:code

id         num  
------  --------
001            5
002            8
003           20
004           35
005           41
006            2
007           15
008           10
009            9

咱們的解題思路也是很是簡單、粗暴,就是把全部物品的可能的組合的重量都算出來,最後只取總量是 50kg 的組合。固然,在這個過程當中能夠作些優化的工做。orm

那怎麼求組合呢?用自關聯。好比,求任意兩種物品的組合,SQL 能夠這麼寫:排序

SELECT 
  * 
FROM
  bag a,
  bag b 
WHERE a.id < b.id

條件 a.id < b.id 用於去掉重複的組合。好比,物品 001 和物品 002,無論是 001 & 002 或者 002 & 001 ,都屬於一個組合。遞歸

咱們能夠像上一篇文章同樣,使用遞歸枚舉出全部的組合。form

WITH RECURSIVE t (id, total, path, formula, next_id) AS 
(SELECT 
  id,
  num AS total,
  CAST(id AS CHAR(100)) AS path,
  CAST(num AS CHAR(100)) AS formula,
  id AS next_id 
FROM
  bag 
UNION ALL 
SELECT 
  t.id,
  t.total + a.num AS total,
  CAST(
    CONCAT(t.path, ' & ', a.id) AS CHAR(100)
  ) AS path,
  CONCAT(
    formula,
    ' + ',
    CAST(num AS CHAR(100))
  ) AS formula,
  a.id AS next_id 
FROM
  t,
  bag a 
WHERE t.next_id < a.id 
  AND t.total + a.num <= 50) 
SELECT 
  formula,
  total,
  path 
FROM
  t 
WHERE total = 50

其中,字段 path 和 formula 只是爲了讓結果看起來更友好,去掉它們對整個計算過程沒有影響。class

關鍵的處理邏輯是這段代碼:next

WITH RECURSIVE t (id, total, next_id) AS 
(SELECT 
  id,
  num AS total,
  id AS next_id 
FROM
  bag 
UNION ALL 
SELECT 
  t.id,
  t.total + a.num AS total,
  a.id AS next_id 
FROM
  t,
  bag a 
WHERE t.next_id < a.id 
  AND t.total + a.num <= 50)

total 是組合中的數值加和的結果,條件 t.next_id < a.id 是爲了保證組合中的物品的編號按必定的順序(從小到大)排序,防止出現重複的組合;條件 t.total + a.num <= 50 提早過濾掉不知足的組合,減小計算的次數。數據

最終的輸出結果 >>>

formula               total  path                         
-------------------  ------  -----------------------------
35 + 15                  50  004 & 007                    
41 + 9                   50  005 & 009                    
5 + 35 + 10              50  001 & 004 & 008              
5 + 8 + 35 + 2           50  001 & 002 & 004 & 006        
5 + 20 + 15 + 10         50  001 & 003 & 007 & 008        
5 + 8 + 20 + 2 + 15      50  001 & 002 & 003 & 006 & 007
相關文章
相關標籤/搜索