/
pancake.py
153 lines (139 loc) · 5.87 KB
/
pancake.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright 2012 Ning Ke
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This program enumerates permutations of n pancakes and the paths through which
# a particular permutation can be sorted by prefix reversal, or "flipping".
# See http://en.wikipedia.org/wiki/Pancake_sorting for details.
#
# Algorithm
# A permutation of n pancakes is represented by a string of n numbers (1 - n),
# where a number represents the rank of the pancake. For example, "1234"
# represents the sorted 4-pancake stack - "1" is the smallest and "4" is the
# largest.
#
# The program starts from the sorted sequence, doing one prefix reversal at a
# time and inserts the resulting sequences into a trie of level k. For example,
# Starting from "1234", with no flipping, sequence "1234" is inserted into
# trie of level 0 (pancake-trie-0). Proceeding to the next level, with one
# flip, "2134", "3214" and "4321" are produced, each one of these are inserted
# into level 1 (pancake-trie-1). The step continues for each of sequence at
# the previous level, resulting pancake tries of levels from 0 to max-flip(n),
# where max-flip(n) is the number of flips required to sort those sequences of n
# pancakes that require the maximum of the flips.
#
# Data structures
# * pancakeTrieList
# List of pancakes tries, the first (head of list) trie is of
# level 0, the second is level 1, and so on.
# * pancakeTrie
# Trie of sequences of pancake stack.
import sys
import os
import ostore
import plist
import ptrie
import pdscache
from oidfs import OidFS
class Pancake(object):
''' A pancake stack of ''numpcakes'' pancakes '''
def __init__(self, numpcakes, pstor, ofs):
if numpcakes <= 0 or numpcakes > 35:
raise ValueError("numpcakes must be between 1 and 35")
self.pstor, self.ofs = pstor, ofs
self.plistObj = plist.Plist(pstor)
self.ptrieObj = ptrie.Ptrie(pstor)
self.numpcakes = numpcakes
self.pcakeTrieList = plist.emptylist
self.pcakeTrieAll = ptrie.Nulltrie
@staticmethod
def sortedSeq(n):
''' Returns a string of sequence from 1 to n. Because decimal system only has
10 digits and n can be more than 9. We use letter 'a' for 10, 'b' for 11,
and so on. This way we can address up to 9 + 26 = 35 pancakes. '''
seq = ""
for i in range(1, n + 1):
if i < 10:
s = "%d" % i
else:
asciiv = ord('a') + (i - 10)
s = chr(asciiv)
seq += ("%s" % s)
return seq
def _mkPcakeTrie(self, curlist):
''' Create a new ptrie ''pcakeTrie'' from the previous level. The first
level is the sorted sequence. ''curlist'' is the current list of pancake
ptries. '''
pcakeTrie = ptrie.Nulltrie
if (curlist is plist.emptylist):
# Level 0 is simply the sorted sequence
s = Pancake.sortedSeq(self.numpcakes)
print " <%s>" % s
print " 1 Total"
self.pcakeTrieAll = self.ptrieObj.insert(self.pcakeTrieAll, s, None)
pcakeTrie = self.ptrieObj.insert(ptrie.Nulltrie, s, None)
return pcakeTrie
# Create current level from the previous level
cnt = 0
prevTrie = self.plistObj.car(curlist)
for node in self.ptrieObj.bfiter(prevTrie):
f = self.ptrieObj.getfields(node)
if f['final']:
prevseq = f['prefix']
#print "Flipping prevseq '%s'" % prevseq
# Now do prefix reversals on ''prevseq'' and insert the
# resulting sequence into ''pcakeTrie'' if it is not a duplicate.
for seq in Pancake.prefix_rev_iter(prevseq):
# Insert seq into pcakeTrieAll, detect duplicate
pt = self.ptrieObj.insert(self.pcakeTrieAll, seq, prevseq, None)
if pt is not self.pcakeTrieAll:
#print " <%s>" % seq
self.pcakeTrieAll = pt
# Got new trie from ''insert'', not a dup. Add seq to
# pcakeTrie also
pcakeTrie = self.ptrieObj.insert(pcakeTrie, seq, prevseq, None)
cnt += 1
print " %d Total" % cnt
return pcakeTrie
@staticmethod
def prefix_rev_iter(tmpl):
''' Make new sequences by doing prefix reversal on ''tmpl''. This is an
iterator. '''
length = len(tmpl)
assert(length > 0)
if length == 1:
yield tmpl
return
for i in range(2, length + 1):
head = tmpl[:i]
tail = tmpl[i:]
yield (head[::-1] + tail)
def build(self):
''' Builds the pancake ptrie list one level at a time. Build stops when
a 'pcakeTrie'' is empty, which means that there are no new sequences left,
in other words, we have enumerated all permutations of the n pancakes. '''
i = 0
while True:
print "Level %d" % i
pcakeTrie = self._mkPcakeTrie(self.pcakeTrieList)
if pcakeTrie is ptrie.Nulltrie:
break
i += 1
self.pcakeTrieList = self.plistObj.cons(pcakeTrie, self.pcakeTrieList)
if __name__ == "__main__":
import sys
num = int(sys.argv[1])
pstor, ofs = ostore.init_ostore()
pcakeObj = Pancake(num, pstor, ofs)
pcakeObj.build()
ofs.close()
pstor.close()