如何在SWI-PROLOG中删除两个相等的相邻数字

时间:2014-12-16 15:02:12

标签: prolog

我是PROLOG的新手,难以达到很少的任务。如果有人能在这方面帮助我,那可能会很棒。所以这就是这个想法:

我有一个包含[3413242341]的数字列表,目标是简化此列表,直到我可以有一个空列表[]。 (数字简化技术)。我把目标搞得如下:

  1. 预测初始化。
  2. (R1)如果两个相邻的数字相等,程序应该删除它们。
  3. (R2)如果差异的绝对值大于1,程序应该能够在两个相邻的数字之间切换。
  4. (R3)(n,n-1,n)序列必须能够被(n-1,n,n-1)序列取代。
  5. 我正在为这个项目奋斗两天,任何帮助都会受到高度关注。

2 个答案:

答案 0 :(得分:0)

prolog的重要之处在于它是一个声明性语言:你用谓词演算来描述解决方案,并让prolog推理引擎找出解决方案。通常,prolog程序看起来很像问题陈述。我们先来吧?

您的问题陈述描述了2个特定规则,并且(恕我直言)有第3个隐含规则:

  • 完全删除两个相邻的相同数字([1,2,2,3][1,3])。
  • 交换序列N,N-1,N([8,5,4,5,9][8,4,5,4,9])。
  • 而且(暗示)是其他任何事情都没有改变。

这会建议一个像这样的解决方案:

reduce( []         , []         ).                               % When the source list is exhausted, we're done.
reduce( [N,N|Ns]   , Rs         ) :-             reduce(Ns,Rs) . % Apply Rule #1
reduce( [N,Q,N|Ns] , [Q,N,Q|Rs] ) :- Q =:= N-1 , reduce(Ns,Rs) . % Apply Rule #2
reduce( [N|Ns]     , [N|Rs]     ) :-             reduce(Ns,Rs) . % Apply Rule #3: (implied)

我当然不知道这是否符合您的要求。另一种方法可能是这样的:

reduce( []     ) .       % The empty list is the desired state
reduce( [N|Ns] ) :-      % otherwise ...
  remove_pairs(Ns,T1) ,  % - apply rule #1 to the entire list  
  do_swaps(T2,T3) ,      % - apply rule #2 to the entire list
  T3 \= [N|Ns] ,         % - if something changed
  reduce( T3 )           % - repeat the exercise with the changed list
  .                      % Easy!

%
% apply Rule #1 to the entire list:
% Remove adjacent pairs such that [1,2,2,3] -> [1,3]
%
remove_pairs( []       , []     ) .
remove_pairs( [X]      , [X]    ) .
remove_pairs( [X,X|Xs] ,    Rs  ) :-          remove_pairs(Ns,Rs) .
remove_pairs( [X,Y|Xs] , [X|Rs] ) :- X \= Y , remove_pairs(Xs,Rs) .

%
% Apply Rule #2 to the entire list:
% Swap N,Q,N where Q is N-1 such that [8,5,4,5,9] -> [8,4,5,4,9]
%
do_swaps( []         , []         ) .
do_swaps( [N]        , [N]        ) .
do_swaps( [N,Q]      , [N,Q]      ) .
do_swaps( [N,Q,N|Ns] , [Q,N,Q|Rs] ) :- Q =:= N-1 , do_swaps(Ns,Rs) .
do_swaps( [N,Q,N|Ns] , [N,Q,N|Rs] ) :- Q =\= N-1 , do_swaps(Ns,Rs) .

这会将每个转换应用于整个列表。如果列表发生了更改,我们会递归应用reduce/1操作,如果列表最终转换为空列表[],则该操作将成功,并且当转换未能进行任何更改时最终会失败。

当回溯时,它会尝试寻找另一种解决方案。

答案 1 :(得分:0)

解决这个问题的另一种方法:

simplify(In, Lst) :-
    simplify(In, [], [], Lst).


simplify([], _Deja_vu, RL, L) :- !, reverse(RL, L).
simplify(In,  Deja_vu, Cur, Lst) :-
    (   r1(In, Out), L =r1; r2(In, Out), L = r2; r3(In, Out), L=r3),
    % we must avoid cycles
    \+member(Out, Deja_vu),
    simplify(Out, [Out| Deja_vu],[L|Cur], Lst).

% If two adjacent numbers are equal, program should remove both of them.
r1(In, Out) :-
    append(L, [X,X|T], In),
    append(L, T, Out).

% switch between two adjacent digits if the absolute value of
% their difference is greater than 1.
r2(In, Out) :-
    append(L, [X,Y|T], In),
    abs(X-Y) > 1,
    append(L, [Y,X|T], Out).

% (n, n-1, n) sequence must be able to be replaced by the (n-1, n, n-1) sequence.
r3(In, Out) :-
    append(L, [X,Y, X|T], In),
    X =:= Y+1,
    append(L, [Y,X,Y|T], Out).