0

So using the itertools module I'm able to code up a really slick bit of code for producing all permutations with replacement, but what I wanted to do was something using recursion.

Here's what I came up with:

def permutations_with_replacement(n,k,permutations):
    m = 0
    if k < 1:
        return permutations

    for i in range(27):                    
        permutations[i].append(m % n)
        if (i % n**(k-1)) == n**(k-1) - 1:
            m = m + 1

    return permutations_with_replacement(n,k-1,permutations)


n = 3
k = 3
permutations = [[] for i in range(n**k)]
print permutations_with_replacement(n,k,permutations)

Basically it sort of lays the first layer (entry) of each permutation, and then upon each subsequent iteration it runs through 0...n-1 quicker and quicker in order to get all combinations. I put in an example with n=k=3 since I have to initialize the permutations list and initializing it inside the function causes things to get screwed up upon recursion. I also put in 27 for the range rather than n^k, since n^k would get screwed up as well upon recursion. How could this be made to work cleanly?

What I really wanted to do was do a recursion that basically replaced the nested for-loop method of producing all permutations with replacement, my understanding being that recursion gets around the problem that the nested for-loop method requires knowing the depth of the nested for-loops a priori. So if anyone can show me how to do it that way, that would be great as well, thanks.

GreenAsJade
  • 14,459
  • 11
  • 63
  • 98
Set
  • 934
  • 6
  • 25

2 Answers2

2

I think that the problem with your approach is that you had not committed, mentally to the recursion. Warning signs are having to create the whole sized array before you start, and finding that you need to know how big the whole thing is inside the body of routine recursive routine.

I came up with the following by thinking:

1) What will the answer of p_w_r be for k=1 and any n? I experimented on paper with n=2

2) Given that thing I think is the answer to 1, how do I make the answer for k=2 starting from the output of k=1.

I had to mess around a bit conceptually before I realised that the answer for k=1 has to be a list of single element lists itself (obvious when you see it, of course). From there, I could see that I needed to "stick this list" onto each element in the k+1 case.

       def permutations_with_replacement(k,n):
         # special case (not part of recursion)
         if k == 0:
            return []

         if k == 1
            return [[i] for i in range(n)]
         else:
            # Make the list by sticking the k-1 permutations onto each number 
            # we can select here at level k    
            result = []
            # Only call the k-1 case once, though we need it's output n times.
            k_take_one_permutations = permutations_with_replacement(k-1,n)  

            for i in range(n):
                for permutation in k_take_one_permutations:
                    result.append([i]+permutation)   
            return result

         print permutations_with_replacement(3,2)



momerath:~ mgregory$ python foo.py
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
momerath:~ mgregory$ 
GreenAsJade
  • 14,459
  • 11
  • 63
  • 98
  • 2
    You don't need a special case for `k==1` if you change `k==0` to return `[[]]`. – Janne Karila Jan 20 '14 at 18:13
  • Thanks!!! I was actually mulling over how to get rid of that special case for ages :) And there it was in front of me the whole time. But, interestingly, spotting it requires thinking about how the whole thing works in a different way ... I was stuck on the idea that the base of it all is the generation of the n vales at k=1! Nice one! – GreenAsJade Jan 20 '14 at 21:18
1

I guess this doesn't help if you want to roll your own solution, but there's a very clean way to do this using itertools.product, which basically is equivalent to a Cartesian product.

import itertools

def permutations_with_replacement(n, k):
    # if you're on Python 2.x, use xrange(n) instead of range(n)
    return itertools.product(range(n), repeat=k)

n, k = 3, 3
for permutation in permutations_with_replacement(n, k):
    print(permutation)

Output:

(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 1, 0)
(0, 1, 1)
# some omitted
(2, 1, 1)
(2, 1, 2)
(2, 2, 0)
(2, 2, 1)
(2, 2, 2)
senshin
  • 10,022
  • 7
  • 46
  • 59
  • If you had read the first line of my post you would have seen that I originally did it using itertools, on the other hand, thank you for acknowledging my existence, I'm so lonely =(. – Set Jan 20 '14 at 08:57
  • @pajamas Well, yes, I read the first line of your post, but seeing as you didn't actually show us your itertools implementation, I had no way of knowing whether or not you were aware of `itertools.product`. – senshin Jan 20 '14 at 08:59
  • @seshin You're right, I probably should have put that implementation in there too. – Set Jan 20 '14 at 09:05