在Prolog中找到N个独特的目标解决方案

时间:2014-07-12 15:39:11

标签: prolog

你能告诉我如何在Prolog中找到N个独特的目标解决方案吗?

我知道使用findall / 3可以找到目标的所有解决方案,但是对于具有太多或无限解决方案的目标,如果足够的话,我想要找到最多N个唯一解决方案。

我想做的是这样的:

?- find_unique_n(10, X, any_goal(X), Xs).
Xs = [...] % up to 10 unique solutions.

如果目标的唯一解决方案总数低于N,我想找到所有这些。

编辑: 正如虚假指出的那样,尚不清楚“独特解决方案”的含义。如果sample_goal / 1定义如下:

sample_goal(1).
sample_goal(1).
sample_goal(2).
sample_goal(2).

预期结果是:

?- find_unique_n(1, X, sample_goal(X), Xs).
Xs = [1]
?- find_unique_n(2, X, sample_goal(X), Xs).
Xs = [1,2]
?- find_unique_n(3, X, sample_goal(X), Xs).
Xs = [1,2]

对于具有无限解决方案的目标,预期结果为:

?- find_unique_n(2, X, (repeat, between(1,2,X)), Xs).
Xs = [1,2]
?- find_unique_n(3, X, (repeat, between(1,2,X)), Xs).
% This won't stop, it's ok

3 个答案:

答案 0 :(得分:4)

答案与解决方案

从你的问题来看,不清楚你的意思是什么"独特的解决方案"。毕竟,你说:

  

如果目标的唯一解决方案总数低于N,我想找到所有这些。

考虑目标(X = 1, repeat)。唯一解决方案的总数是一个。但是,你仍然无法停止,因为你不知道你是否找到了所有的解决方案。因此,如果N大于1,则必须循环。或者考虑( repeat, between(1,10,N) )这里有十个唯一的解决方案,因此如果N低于或等于10,您可以找到它们并终止。

请注意,Prolog会生成可能包含解决方案的答案。通常情况下,您获得的答案替换不一定是基础。想想X = t(_).这个答案包含无限多种解决方案,例如X = t(1)X = t(2)等。

很可能你想看到第一个N答案。解决方法是here

从字面上理解你的问题(即:总是终止目标,地面答案),只需将setof(t,Goal,_)包裹在目标周围。

答案 1 :(得分:4)

这是一个解决方案,虽然不是特别有效。我们的想法是反复调用(副本)目标,寻找尚未列入Sols列表的解决方案:

find_unique_n(N, X, Goal, Xs) :-
    find_unique_n(N, X, Goal, Xs, []).

find_unique_n(N, X, Goal, Xs, Sols) :-
    N > 0,
    copy_term(X-Goal, CX-CGoal),
    call(CGoal),
    \+ (member(Sol,Sols), variant(Sol,CX)),
    !,
    N1 is N-1,
    Xs = [CX|Xs1],
    Sols1 = [CX|Sols],
    find_unique_n(N1, X, Goal, Xs1, Sols1).
find_unique_n(_N, _X, _Goal, [], _Sols).

如果您的解决方案都已完成,您可以使用== / 2代替variant / 2.

或者,如果您的Prolog具有方便的原语来跨回溯保存数据,您可以使用失败驱动的方法,如下面的ECLiPSe示例所示:

find_unique_n(N, X, Goal, Xs) :-
    store_create(Solutions),
    (
        once((
            call(Goal),
            store_set(Solutions, X, _),
            store_count(Solutions) >= N
        )),
        fail
    ;
        stored_keys(Solutions, Xs)
    ).

其中store-primitives实现不可回溯的哈希表。使用断言/撤回的类似解决方案是可能的,但是非折叠可以使重入和内存泄漏。

答案 2 :(得分:2)

在您的澄清之后,这是一个非常紧凑的解决方案,其中还包含一些可重用的谓词,请参阅this answer for call_firstn/2和其他谓词。

find_unique_n(N, X, Goal_0, Xs) :-
   findall(X, call_firstn(call_nub(Goal_0), N), Xs).

所以下面的{n}是call_nub/1。 Nub,如nub

警告:此版本需要setup_call_cleanup/3call_cleanup/2才能正常工作,并且不能与约束一起使用。

:- dynamic(nub_answer_id/2).
:- dynamic(generated_id/1).

:- meta_predicate(call_nub(0)). % only for SICStus/SWI/YAP
call_nub(Goal_0) :-
   setup_call_cleanup(
      genid(Id),
      (  term_to_vec(Goal_0, Vec),
         Goal_0,
         (  \+nub_answer_id(Vec, Id)
         -> true
         ;  \+ ( nub_answer_id(XVec, Id), subsumes_term(XVec, Vec) )
         ),
         asserta(nub_answer_id(Vec, Id))
      ),
      retractall(nub_answer_id(_,Id))
   ).

term_to_vec(T, Vec) :-
   term_variables(T, Vs),
   catch(Vec=..[v|Vs],error(representation_error(max_arity),_),T=Vec).

genid(Id) :-
   (  generated_id(Id0) -> true ; Id0 = 0 ),
   Id1 is Id0 + 1,
   asserta(generated_id(Id1)),
   retractall(generated_id(Id0)),
   Id1 = Id.

setup_call_cleanup(Setup_0, Call_0, Cleanup_0) :- % only for SICStus
   once(Setup_0),
   call_cleanup(Call_0, once(Cleanup_0)).