如何在prolog中最大化目标?

时间:2016-02-16 04:11:57

标签: prolog

我正在尝试解决prolog中的背包问题。以下是我的实施。

% 'ks' is compound term which has 4 argumets                                                                                                                                                                                                   
%   1 - List of items to be chosen from.                                                                                                                                                                                                       
%   2 - Maximum weight a knapsack can carry.                                                                                                                                                                                                   
%   3 - Selected items which sum of weights is less than or equal to knapsack capacity.                                                                                                                                                            
%   4 - The gain after choosing the selected item.                                                                                                                                                                                             


% base conditions where input list contains only one items and                                                                                                                                                                                 
% it is either selected or excluded.                                                                                                                                                                                                           
ks([item(W1, V1)], W, [item(W1, V1)], V1):- W1 =< W.
ks([item(W1, _)], W, [], 0):- W1 > W.

% An item from the input list is chosen in the knapsack.                                                                                                                                                                                       
% In that case, we recurse with smaller list with reduced weight constraint.                                                                                                                                                                  
ks(ItemList, MaxWeight, SelectItems, Gain) :-
    append(Prefix, [item(W1, V1)|Suffix], ItemList),
    append(Prefix, Suffix, RemList),
    NewWeight is MaxWeight - W1,
    W1 =< MaxWeight,
    append([item(W1, V1)], SelectItems1, SelectItems),
    ks(RemList, NewWeight, SelectItems1, Gain1),
    Gain is V1 + Gain1.

% An item from the input list is not chosen in the knapsack.                                                                                                                                                                                   
% In that case, we recurse with smaller list but with the same weight constraint.                                                                                                                                                             
ks(ItemList, MaxWeight, SelectItems, Gain) :-
    append([P1|Prefix], [item(W1, V1)|Suffix], ItemList),
    append([P1|Prefix], Suffix, RemList),
    not(member(item(W1, V1), SelectItems)),
        ks(RemList, MaxWeight, SelectItems, Gain).

程序的输入将是以下项目列表。术语项目(W,V) W是项目的权重,而V是项目的值。目标是最大化给定权重约束的价值。

ks([item(2,3), item(3,4), item(4,5), item(5,8), item(9,10)], 20, List, Gain).
List = [item(2, 3), item(3, 4), item(4, 5), item(5, 8)],
Gain = 20 ;

虽然我能够使用上述程序生成所有项目组合,但我无法编码以找出最大增益。

请问任何人请指出正确的方向吗?

感谢。

3 个答案:

答案 0 :(得分:1)

至少有两件事你可以做,具体取决于你想要如何处理它。

您可以简单地收集所有解决方案并找到最大值。有点像:

?- Items = [item(2,3), item(3,4), item(4,5), item(5,8), item(9,10)],
   findall(Gain-List, ks(Items, 20, List, Gain), Solutions),
   sort(Solutions, Sorted),
   reverse(Sorted, [MaxGain-MaxList|_]).
% ...
MaxGain = 26,
MaxList = [item(9, 10), item(5, 8), item(4, 5), item(2, 3)].

因此,您找到所有解决方案,按Gain排序,然后选择最后一个。这只是一种方法:如果您不介意收集所有解决方案,那么您需要如何从列表中选择所需的解决方案。您可能还想查找所有最大解决方案:有关如何操作的建议,请参阅this question and answers

更清洁的方法是使用约束。正如您对问题的评论所指出的那样,您实际在做什么并不是很清楚,但是要采用像CLP(FD)之类的库。有了它,您可以简单地告诉labeling/2首先查找最大Gain(一旦您在约束条件下表达了问题)。

答案 1 :(得分:1)

我认为,要找到可重复使用的抽象,它是研究编程的重要一点。如果我们有一个subset_set / 2产生回溯所有子集,ks / 4变得非常简单:

subset_set([], _).
subset_set([H|T], Set) :-
  append(_, [H|Rest], Set),
  subset_set(T, Rest).

ks(Set, Limit, Choice, Gain) :-
    subset_set(Choice, Set),
    aggregate((sum(W), sum(G)), member(item(W, G), Choice), (TotWeight, Gain)),
    TotWeight =< Limit.

然后

ks_max(Items, Limit, Sel, WMax) :-
    aggregate(max(W,I), ks(Items,Limit,I,W), max(WMax,Sel)).

尽管它很简单,但subset_set / 2并不容易编码,库可用的替代品(subset / 2,ord_subset / 2)不需要枚举,但只检查关系。

答案 2 :(得分:0)

贪心近似算法:

pw((P,W),Res) :-  PW is P/W, Res=(PW,P,W).
pws(Ps_Ws,PWs) :- maplist(pw,Ps_Ws,PWs).

sort_desc(List,Desc_list) :-
        sort(List,Slist),
        reverse(Slist,Desc_list).



ransack_([],_,_,[]).
ransack_([(_,P,W)|PWs],Const,Sum,Res) :-   
        Sum1 is W+Sum,
        Sum1 < Const ->
          Res=[(P,W)|Res1],
          ransack_(PWs,Const,Sum1,Res1)
        ;ransack_(PWs,Const,Sum,Res).                         


% ransack(+[(P,W)|..],+W,,Res)
ransack(L_PWs,W,Res) :- 
              pws(L_PWs,Aux),
              sort_desc(Aux,PWs),

              ransack_(PWs,W,0,Res).

测试

项目(W,V) - &gt;(V,W)

| ?- ransack([(3,2),(4,3),(5,4),(8,5),(10,9)],20,Res).
Res = [(8,5),(3,2),(4,3),(5,4)] ? ;
no