这里有Prolog的问题。考虑一组产品,每个产品都有给定的价格。为简单起见,我们考虑每种产品的一个项目。假设您有一对对象列表(Item,Price)
,表示商店中商品的名称及其对应的价格。名称是常量,价格是正整数。
例如:
[(radio,130),(tv,940),(laptop,400),(bicycle,330)]
编写一个谓词basket(L,Smin,Smax,S)
,找出L
中可用项目的子集S,使S
中的价格总和高于Smin
且低于Smax
JasperDesign
。
所有可能满足问题规范的子集都应该通过回溯逐一呈现!
答案 0 :(得分:1)
Prolog中的一种习惯,大多数时候都在使用累加器。累加器是递归传递的变量,并在每个递归步骤中更新。每个递归步骤都可以检查变量的值并相应地做出决定。这里的累加器是项目价格的总和这远远增加到S
。显然在开始时,该总和为零,所以我们首先通过引入累加器来缓解一些事情:
basket(L,Smin,Smax,S) :-
basket(L,Smin,Smax,0,S).
现在我们需要找出basket/5
。有一个基本案例,其中列举了所有项目。因此L
为空。那么我们需要做的是检查Sum
是否在给定边界之间:
basket([],Smin,Smax,Sum,[]) :-
Smin < S,
S < Smax.
这是唯一的基本情况。请注意,S
(最后一个参数)也是空列表。由于所有项目都已经过检查,这意味着我们不会在子集的尾部添加任何元素。
现在有两个递归案例。在递归的情况下,L
至少包含一个元素,我们可以做两件事:
如果我们接受该项,谓词如下:
basket([(I,V)|T],Smin,Smax,Sum,[(I,V)|S]) :-
Sum1 is Sum+V,
Sum1 < Smax,
basket(T,Smin,Smax,Sum1,S).
我们决定添加元素,我们通过将值Sum1
添加到原始V
来计算新的总和Sum
,我们可以(可选)已经执行了边界检查值的上限。如果有大量物品,这可能是有效的:我们从包裹开始溢出的那一刻开始切断搜索。
最后我们有另一个递归的情况,我们只是决定丢弃该项目。这很简单:
basket([_|T],Smin,Smax,Sum,S) :-
basket(T,Smin,Smax,Sum,S).
我们甚至没有查看头部,我们只需将列表的尾部移至basket
,我们不必更新Sum
,因为没有更改为子集。
总而言之,以下程序应该可以解决问题:
basket(L,Smin,Smax,S) :-
basket(L,Smin,Smax,0,S).
basket([],Smin,Smax,Sum,[]) :-
Smin < S,
S < Smax.
basket([(I,V)|T],Smin,Smax,Sum,[(I,V)|S]) :-
Sum1 is Sum+V,
Sum1 < Smax,
basket(T,Smin,Smax,Sum1,S).
basket([_|T],Smin,Smax,Sum,S) :-
basket(T,Smin,Smax,Sum,S).
但请注意,使用动态编程可以更有效地生成解决方案。
答案 1 :(得分:1)
当我们从this answer
获得子列表/ 2时sublist([], []).
sublist([_|XS], YS) :-
sublist(XS, YS).
sublist([X|XS], [X|YS]) :-
sublist(XS, YS).
library(aggregate)可用于强制执行约束:
basket(L,Smin,Smax,S) :-
sublist(L,S),
aggregate(sum(P), I^member((I,P),S), T),
T>=Smin,T=<Smax.
?- basket([(radio,130),(tv,940),(laptop,400),(bicycle,330)],1600,2000,S).
S = [(tv, 940), (laptop, 400), (bicycle, 330)] ;
S = [(radio, 130), (tv, 940), (laptop, 400), (bicycle, 330)].