這是一道簡化的揹包問題:有一揹包能容納 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