prolog篮子(L,Smin,Smax,S)

时间:2016-01-06 17:16:12

标签: prolog

这里有Prolog的问题。考虑一组产品,每个产品都有给定的价格。为简单起见,我们考虑每种产品的一个项目。假设您有一对对象列表(Item,Price),表示商店中商品的名称及其对应的价格。名称是常量,价格是正整数。 例如:

[(radio,130),(tv,940),(laptop,400),(bicycle,330)]

编写一个谓词basket(L,Smin,Smax,S),找出L中可用项目的子集S,使S中的价格总和高于Smin且低于Smax JasperDesign。 所有可能满足问题规范的子集都应该通过回溯逐一呈现!

2 个答案:

答案 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)].