如何解决CLP中的背包问题

时间:2019-06-28 16:45:46

标签: prolog

我想知道是否有办法解决CLP(B)中的背包问题。 CLP(B)似乎是合适的,因为打包项目可以建模为布尔变量。

  

示例:
  x1,x2,x3,x4,x5 e {0,1}
  x1 * 12 + x2 * 2 + x3 * 1 + x4 * 1 + x5 * 4 = <15
  最大化x1 * 4 + x2 * 2 + x3 * 2 + x4 * 1 + x5 * 10

我对于如何制定背包容量有限的副作用感到有些困惑。 SWI-Prolog似乎具有weighted_maximum / 3,可以进行优化。

enter image description here
图片来自https://en.wikipedia.org/wiki/Knapsack_problem

2 个答案:

答案 0 :(得分:2)

您可以通过发布新变量来计算重量来对尺寸(重量)约束进行建模,然后使用card约束对背包的容量进行建模,最后使用weighted_maximum/2来最大化目标:

:- use_module(library(clpb)).

knapsack_sample([X1,X2,X3,X4,X5], Maximum):-
  knapsack([X1-12/4,X2-2/2,X3-1/2,X4-1/1,X5-4/10], 15, Maximum).

% Data is a list of BucketVar-Value/Weight
knapsack(Data, Capacity, Maximum):-
  buckets(Data, [], [], Buckets, AndEqAll, Weights, Xs),
  sat(card([0-Capacity], Buckets)),
  sat(AndEqAll),
  weighted_maximum(Weights, Xs, Maximum).

buckets([], [EqAll|LEqAll], LBuckets, Buckets, AndEqAll, [], []):-
  foldl(andall, LEqAll, EqAll, AndEqAll),
  append(LBuckets, Buckets).
buckets([X-Count/Weight|Counts], LEqAll, LBuckets, Buckets, AndEqAll, [Weight|Weights], [X|Xs]):-
  length([B|Bs], Count),
  foldl(eqall(X), Bs, (X=:=B), EqAll),
  buckets(Counts, [EqAll|LEqAll], [[B|Bs]|LBuckets], Buckets, AndEqAll, Weights, Xs).

eqall(B, X, Y, (B=:=X)*Y).

andall(X, Y, X*Y).

因此,在您的示例中,您将以Data=[X1-12/4,X2-2/2,X3-1/2,X4-1/1,X5-4/10]15作为容量的背包:

?- knapsack([X1-12/4,X2-2/2,X3-1/2,X4-1/1,X5-4/10], 15, Maximum).
X1 = 0,
X2 = X3, X3 = X4, X4 = X5, X5 = 1,
Maximum = 15.

更新:

实际上card约束可以很好地处理重复,因此无需添加新变量,解决方案变得更简单:

knapsack2(Data, Capacity, Maximum):-
  maplist(knap, Data, LBuckets, Weights, Xs),
  append(LBuckets, Buckets),
  sat(card([0-Capacity], Buckets)),
  weighted_maximum(Weights, Xs, Maximum).

knap(X-Value/Weight, Ws, Weight, X):-
  length(Ws, Value),
  maplist(=(X), Ws).

样品运行:

?- knapsack2([X1-12/4,X2-2/2,X3-1/2,X4-1/1,X5-4/10], 15, Maximum).
X1 = 0,
X2 = X3, X3 = X4, X4 = X5, X5 = 1,
Maximum = 15.

答案 1 :(得分:1)

我最近在我的CLP(B)中添加了一个约束伪/ 4,类似于来自CLP(FD)的约束scalar_product / 4,它不会创建电路,而是以更传统的方式维护约束。代码如下:

knapsack([X1,X2,X3,X4,X5], M) :-
   pseudo([12,2,1,1,4], [X1,X2,X3,X4,X5], =<, 15),
   weighted_maximum([4,2,2,1,10], [X1,X2,X3,X4,X5], M).

我与card / 2的配方进行了比较。与伪/ 4公式不同,card / 2公式将在引擎盖下创建电路。我的系统和SWI-Proilog都是这种情况:

knapsack3([X1,X2,X3,X4,X5], M) :-
   sat(card([0-15],[X1,X1,X1,X1,X1,X1,X1,X1,X1,X1,X1,X1,X2,X2,X3,X4,X5,X5,X5,X5])),
   weighted_maximum([4,2,2,1,10], [X1,X2,X3,X4,X5], M).

我做了一些测试,还测量了模型的建立时间。在本示例中,新的伪/ 4约束似乎是赢家。这是我系统中的结果:

Jekejeke Prolog 3, Runtime Library 1.3.8 (May 23, 2019)

?- time((between(1,100,_), knapsack(_,_), fail; true)).
% Up 95 ms, GC 3 ms, Thread Cpu 93 ms (Current 07/05/19 20:03:05)
Yes

?- time((between(1,100,_), knapsack3(_,_), fail; true)).
% Up 229 ms, GC 5 ms, Thread Cpu 219 ms (Current 07/05/19 20:02:58)
Yes

这是SWI-Prolog中的结果:

?- time((between(1,100,_), knapsack3(_,_), fail; true)).
% 8,229,000 inferences, 0.656 CPU in 0.656 seconds (100% CPU, 12539429 Lips)