We can write permutations(t)
of any iterable t
using inductive reasoning -
- if
t
is empty, yield the empty permutation, ()
- (inductive)
t
has at least one element. For all p
of the recursive sub-problem permutations(t[1:])
, for all i
of inserts(p, t[0])
, yield i
def permutations(t):
if not t:
yield () # 1. empty t
else:
for p in permutations(t[1:]): # 2. at least one element
for i in inserts(p, t[0]):
yield i
Where inserts(t, x)
can be written using inductive reasoning as well -
- If the input
t
is empty, yield the final insert, (x)
- (inductive)
t
has at least one element. yield x
prepended to t
and for all i
of the recursive sub-problem inserts(t[1:], x)
yield t[0]
prepended to i
def inserts(t, x):
if not t:
yield (x,) # 1. empty t
else:
yield (x, *t) # 2. at least one element
for i in inserts(t[1:], x):
yield (t[0], *i)
t = ("","","","")
for p in permutations(t):
print("".join(p))
Python has strong support for generators and offers delegation using yield from ..
. We can simplify the above definitions while maintaining the exact same behaviour -
def permutations(t):
if not t:
yield ()
else:
for p in permutations(t[1:]):
yield from inserts(p, t[0]) # <-
def inserts(t, x):
if not t:
yield (x,)
else:
yield (x, *t)
yield from map(lambda r: (t[0], *r), inserts(t[1:], x)) # <-
Generators are a good fit for combinatorics because working with them is natural and straightforward -
# find all permutations where red is left of green
for p in permutations(t):
if p.index("") < p.index(""):
print("".join(p))
# find all permutations where blue and yellow are adjacent
for p in permutations(t):
if abs(p.index("") - p.index("")) == 1:
print("".join(p))
Additionally generators can be paused/stopped at any time, allowing us to skip potentially millions of computations for significantly large problems -
# which permutation is in rainbow order?
for (n, p) in enumerate(permutations(t)):
if p == ("", "", "", ""):
print(f"permutation {n} is in rainbow order, {p}")
break # <- stops generating additional permutations
permutation 16 is in rainbow order, ('', '', '', '')