用sql實現揹包問題

揹包問題是一道經典的組合優化問題,給定一組物品,每種物品都有本身的重量和價格,在限定的總重量內,如何選擇使得物品的總價格最高,揹包問題又能夠分紅01揹包,徹底揹包和多重揹包等,網上關於這個問題的解答有不少,可是目前發現沒有sql版本的,接下來我就爲你們提供一下sql版本的解法,若是你們有更好的方法,歡迎評論。mysql

一.填滿揹包

首先從最簡單揹包問題入手,給定揹包的體積,每件物品只有一個,如何拿取物品能夠填滿揹包
建立測試數據,vol表明物品的體積sql

CREATE TABLE `bag0`  (
  `vol`   int(0) NULL DEFAULT NULL
);

INSERT INTO `bag0` VALUES (17);
INSERT INTO `bag0` VALUES (9);
INSERT INTO `bag0` VALUES (14);
INSERT INTO `bag0` VALUES (4);
INSERT INTO `bag0` VALUES (16);
INSERT INTO `bag0` VALUES (7);
INSERT INTO `bag0` VALUES (18);
INSERT INTO `bag0` VALUES (19);
INSERT INTO `bag0` VALUES (5);
INSERT INTO `bag0` VALUES (12);

解決這個問題的思路比較簡單,因爲每件物品只有一個,只須要從體積小的物品不斷向體積大的物品累加,最後取知足揹包體積組合便可,使用mysql中的with表達式便可實現,假設揹包的體積爲50,sql以下測試

with recursive bag_fill as
 (select vol, vol as sum_vol,cast(vol as char(500)) as compose
    from bag0
  union
  select t.vol,t.vol+s.sum_vol, concat(t.vol,'-',s.compose)
    from bag0 t, bag_fill s
   where t.vol < s.vol)
select sum_vol,compose
  from bag_fill
 where sum_vol = 50;

二.01揹包

第二個揹包問題是01揹包,給定一組物品,每種物品都有本身的重量和價格,而且每種物品只有一個,在限定的總重量內,如何選擇使得物品的總價格最高
一樣的,建立測試數據,此次多了價格列優化

CREATE TABLE `bag1`  (
  `vol`   int(0) NULL DEFAULT NULL,
  `price` int(0) NULL DEFAULT NULL
);

INSERT INTO `bag1` VALUES (17, 50);
INSERT INTO `bag1` VALUES (9, 31);
INSERT INTO `bag1` VALUES (14, 94);
INSERT INTO `bag1` VALUES (4, 11);
INSERT INTO `bag1` VALUES (16, 68);
INSERT INTO `bag1` VALUES (7, 50);
INSERT INTO `bag1` VALUES (18, 14);
INSERT INTO `bag1` VALUES (19, 43);
INSERT INTO `bag1` VALUES (5, 37);
INSERT INTO `bag1` VALUES (12, 51);

解決這個問題的思路跟上一題差很少,一樣是從體積小的物品不斷向體積大的物品累加體積和價格,取整體積小於等於揹包體積,最後按照總價格倒序排列便可
假設揹包的體積是50,mysql中寫法以下code

with recursive bag_10 as
 (select vol,
         vol as sum_vol,
         price as sum_price,
         cast(vol as char(500)) as compose
    from bag1
  union
  select t.vol,
         s.sum_vol + t.vol,
         s.sum_price + t.price,
         concat(t.vol, '-', s.compose)
    from bag1 t, bag_10 s
   where t.vol < s.vol
     and s.sum_vol + t.vol <= 50)
select sum_vol,sum_price,compose from bag_10 order by sum_price desc;

三.徹底揹包

下一個問題是徹底揹包,與01揹包惟一不一樣的地方就是每種物品的個數不限,此次用01揹包的數據就能夠
其實實現的思路差很少,只不過在遞歸關聯的過程當中,由體積小的物品向體積大的物品累加變成了體積小的物品向體積大或者體積相同的物品累加,假設揹包的體積爲50,mysql的實現方法以下blog

with recursive bag_complete as
 (select vol,
         vol as sum_vol,
         price as sum_price,
         cast(vol as char(500)) as compose
    from bag1
  union
  select t.vol,
         s.sum_vol + t.vol,
         s.sum_price + t.price,
         concat(t.vol,'-', s.compose)
    from bag1 t, bag_complete s
   where t.vol <= s.vol
     and s.sum_vol + t.vol <= 50)
select sum_vol, sum_price,compose from bag_complete order by sum_price desc;

四.多重揹包

最後一個問題是多重揹包,與徹底揹包不一樣的地方是每種物品的個數有限,此次再建立一張數據表,增長一列表明每種物品的數量遞歸

CREATE TABLE `bag2`  (
  `vol` int(0) NULL DEFAULT NULL,
  `price` int(0) NULL DEFAULT NULL,
  `num` int(0) NULL DEFAULT NULL
);

INSERT INTO `bag2` VALUES (17, 50, 1);
INSERT INTO `bag2` VALUES (9, 31, 1);
INSERT INTO `bag2` VALUES (14, 94, 2);
INSERT INTO `bag2` VALUES (4, 11, 3);
INSERT INTO `bag2` VALUES (16, 68, 2);
INSERT INTO `bag2` VALUES (7, 50, 2);
INSERT INTO `bag2` VALUES (18, 14, 3);
INSERT INTO `bag2` VALUES (19, 43, 2);
INSERT INTO `bag2` VALUES (5, 37, 3);
INSERT INTO `bag2` VALUES (12, 51, 3);

多重揹包相對麻煩一些,直接按照上面的方法遞歸關聯是沒法取到全部物品的組合的,個人思路是這樣,首先取出每一個物品的本身與本身關聯的全部組合,例如體積爲4的物品有3個,那麼這個物品的全部組合就是4,4-4,4-4-4,而後在用這個組合的結果按照以前的辦法由體積小的物品向體積大的物品累加
第一個步驟的sql以下,sum_vol,sum_price分別表明物品與本身組合後的整體積和總價格ip

with recursive bag_compose as
 (select vol as raw_vol,
         price,
         num,
         vol * num as sum_vol,
         price * num as sum_price,
         reverse(substr(reverse(repeat(concat(vol, '-'), num)), 2)) as compose
    from bag2
  union
  select raw_vol,
         price,
         num - 1,
         raw_vol * (num - 1),
         price * (num - 1),
         reverse(substr(reverse(repeat(concat(raw_vol, '-'), num - 1)), 2)) as compose
    from bag_compose
   where num > 1)
 select * from bag_compose order by compose;
而後按照原來的方法,從體積小的物品向體積大的物品累加,就能夠求出多重揹包的結果了
with recursive bag_compose as
 (select vol as raw_vol,
         price,
         num,
         vol * num as sum_vol,
         price * num as sum_price,
         reverse(substr(reverse(repeat(concat(vol, '-'), num)), 2)) as compose
    from bag2
  union
  select raw_vol,
         price,
         num - 1,
         raw_vol * (num - 1),
         price * (num - 1),
         reverse(substr(reverse(repeat(concat(raw_vol, '-'), num - 1)), 2)) as compose
    from bag_compose
   where num > 1),
bag_multiple as
 (select raw_vol,
         sum_vol,
         sum_price,
         cast(compose as char(500)) as compose
    from bag_compose as t
   where sum_vol <= 50
  union
  select t.raw_vol,
         s.sum_vol + t.sum_vol,
         s.sum_price + t.sum_price,
         concat(t.compose, '-', s.compose)
    from bag_compose t, bag_multiple s
   where t.raw_vol < s.raw_vol
     and s.sum_vol + t.sum_vol <= 50)
select compose,
       sum_vol,
       sum_price
  from bag_multiple
 order by sum_price desc;

以上就是揹包問題mysql版本的解法,你們有更好的方法,歡迎評論。it

相關文章
相關標籤/搜索