Prolog中的图表

时间:2017-10-06 14:39:09

标签: list graph prolog knapsack-problem

我是Prolog的新手,我需要一些帮助:D

我学会了递归,我知道如何使用它(或多或少)。 我遇到了图表问题。我正在尝试解决背包问题,所以我正在迈出一步。

我的问题: 我有一个类型列表,我想制作长度为n(= 3)的所有子列表,并选择一个具有最大值的子列表。我想我需要一个函数来拉出类型列表的头部并将其传递给另一个函数,该函数递归地计算“儿子”。我的想法是这样的:

append([],L2,L2):- !.
append([T|C],L2,[T|L3]):-
    append(C,L2,L3).

genera_ext(_,[],_).

genera_ext(Padre,[TT|CT],Figlio):-
    genera(Padre,TT,[TT|CT],Figlio),
    genera_ext(Padre,CT,[]).

genera(Padre,Elem,L_tipi,Figlio):-
    append(Padre,[Elem],Base),
    copy_term(Figlio,Base),
    length(Base,Lun),
    Lun =< 3,
    genera_ext(Base,L_tipi,Temp),
    total_ing(Temp,I_Temp),
    total_ing(Base,I_Base),
    I_Temp >= I_Base,
    copy_term(Figlio,Temp),
    nl,write("Figlio = "),write(Figlio).

genera(_,_,_,_).

显然有些不对劲。你可以帮帮我吗?谢谢 :( M.R。

编辑:

我有一些事实

art(xxx,weight_xxx).

这是计算由元素xxx

组成的列表的权重的函数
total_ing([],0).
total_ing([X|C],I0):-
    art(X,N),
    total_ing(C,I1),
    I0 is I1 + N.

我称之为

genera_ext([],L_tipi, Figlio)

其中L_tipi是我可以选择的元素列表xxx。

我想生成长度为3的xxx元素的所有可能子列表,并选择权重最大的元素。

1 个答案:

答案 0 :(得分:1)

  

我想生成长度为3的xxx元素的所有可能子列表,并选择权重最大的元素。

这是一个经典的“生成和测试”问题。你可以用一种天真的方式解决它,通过产生所有可能的艺术排列,用这样的东西:

inefficient([A1,A2,A3], Sum) :-
    art(A1, X),
    art(A2, Y),  A2 \= A1,
    art(A3, Z),  A3 \= A2, A3 \= A1,
    Sum is X+Y+Z.

inefficient_best(L, Sum) :-
    inefficient(L, Sum),
    \+ (inefficient(L2, Sum2), L2 \= L, Sum2 > Sum).

称这种效率低下是非常友善的;它确实是多次尝试每个排列对抗其他所有排列,并且它将生成多个解决方案,这些解决方案只是相同的序列置换。但它确实“有效”。

接下来要考虑的是如何提高效率。没有明显的事情要做,以使测试更快,但生成步骤肯定会产生一堆浪费的组合。我们可以做的第一件事是使用findall/3将数据库实现到列表中,然后使用permutation/2生成排列以尝试。但这对我来说就像它会一样糟糕。我开始认为最好的方法是制作一个谓词来生成一定长度的组合。我想不出一个更好的方法来使用内置的谓词(也许有一个,我只是不够聪明),但我提出了这个:

combination(0, _, []) :- !.
combination(N, [X|Xs], [X|Ys]) :-
    succ(N0, N),
    combination(N0, Xs, Ys).
combination(N, [_|Xs], Ys) :-
    combination(N, Xs, Ys).

这会产生如下结果:

?- combination(3, [a,b,c,d,e], X).
X = [a, b, c] ;
X = [a, b, d] ;
X = [a, b, e] ;
X = [a, c, d] ;
X = [a, c, e] ;
X = [a, d, e] ;
X = [b, c, d] ;
X = [b, c, e] ;
X = [b, d, e] ;
X = [c, d, e] ;
false.

这种方式的工作方式基本上是获取列表中的当前项目并减少我们仍然需要的长度,或者在不获取当前项目的情况下重复。这很像member/2

现在我们已经实现了这一点,我们可以实现数据库,并且可以减少尝试所有排列的工作量。事实上,我们可以使用sort/2来找到胜利者,假设您只需要一个结果,但我们首先需要辅助函数:

art_cost(ArtList, Cost-ArtList) :-
    maplist(art, ArtList, CostList),
    sumlist(CostList, Cost).

art_cost/2计算艺术品清单的总费用,但会返回一对:实际艺术品清单的成本。这种事情并不是非常罕见,我们在下一步中依赖它来为我们的谓词找到最高的成本:

best(ArtList, Cost) :-
    % materialize the list of all artworks
    findall(A, art(A,_), Art),

    % materialize the list of candidate combinations
    findall(Candidate, combination(3, Art, Candidate), Candidates),

    % compute the cost+list for all the candidate lists
    maplist(art_cost, Candidates, CostsWithArtLists),

    % sort the list, which will put them in order of cost
    % but lowest-to-highest
    sort(CostsWithArtLists, CostsLowToHigh),

    % flip the list around and deconstruct the highest 
    % as our result
    reverse(CostsLowToHigh, [Cost-ArtList|_]). 

使用library(aggregate)可能有一种更有效的方法,但我无法弄明白。

顺便说一下,我认为你的代码中根本不需要copy_term/2,当我看到看起来像一堆匿名变量的术语时,我总是怀疑:genera(_,_,_,_) - 对我而言,你似乎不太可能意味着“属于任何四件事情。”