Reputation: 94
i'm a a programmer, i do some C# perl and python, anyways, i found this recursive code to generate permutations, of a list of symbols and letters, but i can't figure out how it works? can anyone care to explain it please?
#!/usr/bin/env python
#-*- coding:utf-8 -*-
def generate(charlist,state,position):
for i in charlist:
state[position] = i
if position == (len(state)-1):
print "".join(state)
else:
generate(charlist,state,position+1)
generate("1234567890qwertyuiopasdfghjklzxcvbnm",[None]*8,0)
here is the code, with all correct spacing.
Upvotes: 1
Views: 282
Reputation: 151177
This does not generate permutations. It generates n-dimensional cartesian products. (In the process, it also generates all permutations, but the algorithm to generate only permutations would be different.)
It's a bit difficult to explain how it works, but if you look at the output for small input, you can see what's going on. Consider the output for 'abc'
and [None] * 3
(I modified the code to act as a true generator):
>>> def generate(charlist,state,position):
... for i in charlist:
... state[position] = i
... if position == (len(state)-1):
... yield "".join(state)
... else:
... for j in generate(charlist,state,position+1):
... yield j
...
>>> print list(generate('abc', [None] * 3, 0))
['aaa', 'aab', 'aac', 'aba', 'abb', 'abc', 'aca', 'acb', 'acc',
'baa', 'bab', 'bac', 'bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc',
'caa', 'cab', 'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']
As you can see, what happens is that initially, generate
calls itself three times, incrementing position
each time (from 0
to 1
to 2
). Each time through the recursion-loop, it puts 'a'
in the current position and tests to see if it has reached the end of the state
list. If so, it yields the result and does not call itself.
In this case, when that happens, position == 2
. Now the for
loop kicks in, storing 'b'
and 'c'
in state[2]
and yielding each of those states. Then the function ends, and control is returned to the caller, for which position == 1
. The caller then continues through its for loop; it sets state[1] = 'b'
and then, since position
is no longer at the end of the state
list, it calls itself again... now position == 2
and the for loop sets state[2] == 'a'
, 'b'
, 'c'
, and so on.
By the way, if you want to compute cartesian products in python, here's a nice way that doesn't require your readers to parse out a recursive algorithm:
>>> import itertools
>>> [''.join(c) for c in itertools.product('abc', 'abc', 'abc')]
['aaa', 'aab', 'aac', 'aba', 'abb', 'abc', 'aca', 'acb', 'acc',
'baa', 'bab', 'bac', 'bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc',
'caa', 'cab', 'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']
You could also do
>>> [''.join(c) for c in itertools.product(*['abc'] * 3)]
Upvotes: 3
Reputation: 34142
The function outputs every possible (unless the third argument is non-zero) combination of given characters.
It takes as the input:
If the second argument is a list of 8 None
values (because [None] * 8 == [None, None, None, None, None, None, None, None]
), setting third argument to non-zero value will cause TypeError
.
@senderle's answer explains what happens in the function.
I should say that this is an example of non-Pythonic code, precisely because it's hard to tell its purpose from the first sight.
Upvotes: 1
Reputation: 213115
You can't figure out how it works? Put this line just after the def generate...
:
print charlist, state, position
and it will tell you what variables are used at each call.
Upvotes: 2