简体   繁体   中英

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.

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.

I am new to prolog and I am lost, what's the idea behind such implementation?

Here's my approach:

1. pancake predicate takes the initial state list, and gives back the different flips till it reaches a final state.

2. Using splitSet and permute predicates to generate the flips based on the length of the initial 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. 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 ). 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. Now the largest pancake lies on top ( [Max|Owl] ), so the whole heap has to be flipped ( 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. If there are no pancakes on the heap left, the heap is sorted ( 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(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 */

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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