如何在prolog中最大化目标?

如何在prolog中最大化目标?,prolog,Prolog,我试图用prolog解决背包问题。下面是我的实现 % 'ks' is compound term which has 4 argumets

我试图用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).
虽然我能够用上面的程序生成所有项目的组合,但我无法仅通过编码来找出最大增益

谁能告诉我正确的方向吗


谢谢。

您至少可以做两件事,这取决于您想如何处理这件事

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

?- 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)].
因此,您可以找到所有解决方案,按
增益对它们进行排序,然后选择最后一个。这只是一种方法:如果您不介意收集所有的解决方案,那么您可以从列表中选择所需的解决方案。您可能还希望找到所有最大的解决方案:请参阅,以了解如何做到这一点


更干净的方法是使用约束。正如对你的问题的评论所指出的,你实际上在做什么还不是很清楚,但是要走的路是使用一个像这样的库。有了它,你可以简单地告诉自己先寻找最大的
增益(一旦你用约束表达了你的问题)。

贪婪近似算法:

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).

我认为寻找可重用的抽象是研究编程的一个重要方面。如果我们有一个子集_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.

尽管子集集/2很简单,但编码起来并不容易,库中可用的替代项(子集/2,ord子集/2)不枚举,只检查关系。

我发现您的代码非常混乱,而且似乎从来都不是背包问题。前两条条款应该怎么做?试着用简单的语言描述它们English@CapelliC我已经添加了注释和解释。如果需要更多评论,请告诉我。干得好。如果您的Prolog有,您可以编写
ks_max(Items,Limit,Sel,WMax):-aggregate(max(W,I),ks(Items,Limit,I,W),max(WMax,Sel))。
@capelical我正在使用SWI Prolog,它有聚合库,您的解决方案工作得很好。谢谢。在您的示例运行中,结果不是最佳结果。优化结果为[(8,5),(3,2),(10,9),(5,4)]。贪婪算法不是最优的,而是快速的:)
| ?- ransack([(3,2),(4,3),(5,4),(8,5),(10,9)],20,Res).
Res = [(8,5),(3,2),(4,3),(5,4)] ? ;
no
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)).