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