繁体   English   中英

Prolog 递归煎饼排序

[英]Prolog Recursive Pancake Sorting

我正在我的一个项目中工作,我想在 Prolog 中实现煎饼排序,到目前为止,我已经完成了算法的大部分内容,但我需要创建将可能的 state(排列)转换为另一个的“函数”。

initial_state([3,2,1]).
final_state([1,2,3]).

dfs(States):-
    initial_state(State),
    depth_first_search(State, [State], State).

depth_first_search(State,States,States):-
    final_state(State).

depth_first_search(State1,SoFarStates,States):-
    move(State1,State2),
    \+ member(State2,SoFarStates),
    append(SoFarStates,[State2],NewSoFarStates),
    depth_first_search(State2,NewSoFarStates,States).

所以我想在 move(State1,State2) 中使用一些递归,因此只要在“SoFarStates”中不存在特定排列(如果它执行计数器(?)翻转),就会在一定次数的翻转后创建 State2现在第一个 n-1 并再次检查。

我是 prolog 的新手,我迷路了,这种实现背后的想法是什么?

这是我的方法:

1. pancake 谓词获取最初的 state 列表,并返回不同的翻转,直到达到最终的 state。

2.使用 splitSet 和 permute 谓词根据初始 state 的长度生成翻转。

pancake([H|T],FlipState):-
    splitSet([H|T],_,Sublist),
    Sublist=[_|_], /* disallow empty list */
    permute(Sublist,N1),
    length([H|T],Len1),
    length(N1,Len2),
    Len1=Len2,
    FlipState=N1.

splitSet([ ],[ ],[ ]).
splitSet([H|T],[H|L],R) :-
    splitSet(T,L,R).
splitSet([H|T],L,[H|R]) :-
    splitSet(T,L,R).

permute([ ],[ ]) :- !.
permute(L,[X|R]) :-
    omit(X,L,M),
    permute(M,R).

omit(H,[H|T],T).
omit(X,[H|L],[H|R]) :-
    omit(X,L,R).

例子:-

?-pancake([3,2,1],P)
P = [3, 2, 1]
P = [3, 1, 2]
P = [2, 3, 1]
P = [2, 1, 3]
P = [1, 3, 2]
P = [1, 2, 3]

?-pancake([322,6],P)
P = [322, 6]
P = [6, 322]
false
maximum([A],A,N,N,A).
maximum([H|T],A,N,Posneu,Last):-
    NN is N+1,
    maximum(T,B,NN,Pos,Last),
    (   H>B
    ->  A=H,
        Posneu = N
    ;   A=B,
        Posneu = Pos).

pancakesort([], []).
pancakesort(L, Lsort):-
    maximum(L,Max,1,PosMax,Last),
    (   Last == Max
    ->  append(Lwo,[Max],L)
    ;   length(Ltmp,PosMax),
        append(Ltmp,Ltmp1,L),
        reverse(Ltmp,Pmtl),
        append(Pmtl,Ltmp1,[Max|Owl]),
        reverse(Owl,Lwo)
    ),
    pancakesort(Lwo,LwoSort),
    append(LwoSort,[Max],Lsort).
    
?- pancakesort([1,4,2,6],L).
L = [1, 2, 4, 6] ;
false.

所以每个迭代/递归步骤的想法是找到最大值,翻转包含最大值作为最后一个元素的堆,以便最大的煎饼在顶部,然后再次翻转它,使最大的煎饼在底部。 重复煎饼堆,没有最下面的煎饼。

因此,对于我的实现,我需要一个辅助谓词maximum/5 ,它在列表中搜索最大值并返回最大值的 position。 为了让它更有效率,它还返回最后一个元素,以便程序首先检查最大的煎饼是否已经在底部( Last == Max )。 如果不是这种情况,则将第一个PosMax煎饼 ( Ltmp ) 从堆中分离出来,翻转 ( reverse(Ltmp,Pmtl) ) 并放回堆上。 现在最大的煎饼位于顶部( [Max|Owl] ),因此必须翻转整个堆( reverse(Owl,Lwo) )。 由于最大的煎饼将被移除,因此在翻转之前更容易将其移除。 现在对未排序的堆重复该过程以获得排序的子堆。 Append 最后的最大元素,你得到一个排序列表。 如果剩下的堆上没有煎饼,则对堆进行排序( pancakesort([], []).

如果最大的煎饼已经在底部,则更紧凑但效率更低的实现将忽略该案例的测试:

maximum([A],A,N,N).
maximum([H|T],A,N,Posneu):-
    NN is N+1,
    maximum(T,B,NN,Pos),
    (   H>B
    ->  A=H,
        Posneu = N
    ;   A=B,
        Posneu = Pos).

pancakesort([], []).
pancakesort(L, Lsort):-
    maximum(L,Max,1,PosMax),
    length(Ltmp,PosMax),
    append(Ltmp,Ltmp1,L),
    reverse(Ltmp,Pmtl),
    append(Pmtl,Ltmp1,[Max|Owl]),
    reverse(Owl,Lwo),
    pancakesort(Lwo,LwoSort),
    append(LwoSort,[Max],Lsort).

你的答案不正确,但它们对于深入了解我真正想要的东西至关重要。 这是move的实现。

move(State1, State2) :-
    length(State1, N1),              /* Number of pancakes plus the plate */
    N is N1 - 1,                                    /* Number of pancakes */
    between(1, N, Operator),               /* Select a pancake to reverse
                                                 the whole stack above it */
    State1 \= [Operator|_],         /* This should not be the top pancake */
    append(Prefix, [Operator|Rest], State1),    /* Isolate pancakes above
                                               the one acting as operator */
    reverse(Prefix, RevPrefix),                           /* Reverse them */
    append([Operator|RevPrefix], Rest, State2).  /* Build the final stack */

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM