Here's a version of your code that uses an extra argument to remember the start position of the list. I changed your function into a recursive generator, so it yields the values, rather than printing them. I also changed high
to stop
, which is consistent with the convention used in Python's slicing eg, seq[start:stop]
, and in range(start, stop)
.
def permute(lst, start, stop, low=None):
if low == stop - 1:
yield lst[start:stop]
if low is None:
low = start
for index in range(low, stop):
lst[index], lst[low] = lst[low], lst[index]
for t in permute(lst, start, stop, low + 1):
yield t
lst[index], lst[low] = lst[low], lst[index]
# Test
lst = list(range(5))
print(list(permute(lst, 1, 4)))
for t in permute(lst, 0, 3):
print(t)
output
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
[0, 1, 2]
[0, 2, 1]
[1, 0, 2]
[1, 2, 0]
[2, 1, 0]
[2, 0, 1]
That code works identically on Python 2 & 3, although in Python 3 it can be made a little more efficient (and more compact) by using the yield from
syntax. Replace that for t in permute
... loop with
yield from permute(lst, start, stop, low + 1)
In Python 3 you can also create a list of the values in a more compact way:
[*permute(lst, 1, 4)]
Finally, here's another recursive generator built from your algorithm, but it permutes the whole list. Of course, you can call it with an interface function to permute a sublist.
def permuter(lst):
if not lst:
yield lst
for index in range(len(lst)):
lst[index], lst[0] = lst[0], lst[index]
first = lst[:1]
for t in permuter(lst[1:]):
yield first + t
lst[index], lst[0] = lst[0], lst[index]
def permute(lst, start, stop):
yield from permuter(lst[start:stop])
lst = list(range(5))
print(list(permute(lst, 1, 4)))