简体   繁体   English

Prolog 递归煎饼排序

[英]Prolog Recursive Pancake Sorting

I am working in a project of mine and I want to implement pancake sorting in Prolog, so far I have made most parts of the algorithm but I need to create the 'function' that converts a possible state (permutation) to another.我正在我的一个项目中工作,我想在 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).

So I was thinking to use some recursion in the move(State1,State2) so the State2 is created after a certain number of flips as long as that specific permutation does not exist in the 'SoFarStates' if it does a counter (?) flips the first n-1 now and checks again.所以我想在 move(State1,State2) 中使用一些递归,因此只要在“SoFarStates”中不存在特定排列(如果它执行计数器(?)翻转),就会在一定次数的翻转后创建 State2现在第一个 n-1 并再次检查。

I am new to prolog and I am lost, what's the idea behind such implementation?我是 prolog 的新手,我迷路了,这种实现背后的想法是什么?

Here's my approach:这是我的方法:

1. pancake predicate takes the initial state list, and gives back the different flips till it reaches a final state. 1. pancake 谓词获取最初的 state 列表,并返回不同的翻转,直到达到最终的 state。

2. Using splitSet and permute predicates to generate the flips based on the length of the initial 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).

Example:-例子:-

?-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.

So the idea in each iteration/recursion step is to find the maximum, flip the heap which contains the maximum as last element so that the largest pancake is on top and then to flip it again so that the largest pancake is on the bottom.所以每个迭代/递归步骤的想法是找到最大值,翻转包含最大值作为最后一个元素的堆,以便最大的煎饼在顶部,然后再次翻转它,使最大的煎饼在底部。 Repeat with the pancake heap without the lower most pancake.重复煎饼堆,没有最下面的煎饼。

So for my implementation I need a helper predicate maximum/5 which searches for the maximum in a list and returns the position of the maximum as well.因此,对于我的实现,我需要一个辅助谓词maximum/5 ,它在列表中搜索最大值并返回最大值的 position。 To make it a bit more efficient it also returns the last element so that the program check first if the largest pancake is already on the bottom ( Last == Max ).为了让它更有效率,它还返回最后一个元素,以便程序首先检查最大的煎饼是否已经在底部( Last == Max )。 If this is not the case the first PosMax pancakes ( Ltmp ) are seperated from the heap, flipped ( reverse(Ltmp,Pmtl) ) and put back on the heap.如果不是这种情况,则将第一个PosMax煎饼 ( Ltmp ) 从堆中分离出来,翻转 ( reverse(Ltmp,Pmtl) ) 并放回堆上。 Now the largest pancake lies on top ( [Max|Owl] ), so the whole heap has to be flipped ( reverse(Owl,Lwo) ).现在最大的煎饼位于顶部( [Max|Owl] ),因此必须翻转整个堆( reverse(Owl,Lwo) )。 Since the largest pancake will be removed it is easier to remove it before flipping.由于最大的煎饼将被移除,因此在翻转之前更容易将其移除。 Now the procedure is repeated for the unsorted heap getting a sorted subheap.现在对未排序的堆重复该过程以获得排序的子堆。 Append the maximium element at the end and you got a sorted list. Append 最后的最大元素,你得到一个排序列表。 If there are no pancakes on the heap left, the heap is sorted ( pancakesort([], []). )如果剩下的堆上没有煎饼,则对堆进行排序( pancakesort([], []).

A more compact but also more inefficient implementation would ignore the test for the case if the largest pancake is already at the bottom:如果最大的煎饼已经在底部,则更紧凑但效率更低的实现将忽略该案例的测试:

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).

Your answers were not correct, but they were crucial in order to undestand in depth what i was actually wanted.你的答案不正确,但它们对于深入了解我真正想要的东西至关重要。 This is the implementation of move.这是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