12

I need a kick in the head on this one. I have the following recursive function defined:

def perms(s):
  if(len(s)==1):
    return s

  res = ''
  for x in xrange(len(s)):

    res += s[x] + perms(s[0:x] + s[x+1:len(s)])

  return res + '\n'

perms("abc") currently returns:

abccb
bacca
cabba

The desired result is

abc
acd
bac
bca
cab
cba

Where am I going wrong here? How can I think about this differently to come up with the solution?

Note: I am aware of the itertools function. I am trying to understand how to implement permutations recursively for my own learning. That is why I would prefer someone to point out what is wrong with my code, and how to think differently to solve it. Thanks!

gnp210
  • 153
  • 1
  • 1
  • 9

6 Answers6

15

The result of permutations will be a collection, let's say a list. It will make your code cleaner if you think this way and if required you can join the results into a single string. A simple example will be

def perms(s):        
    if(len(s)==1): return [s]
    result=[]
    for i,v in enumerate(s):
        result += [v+p for p in perms(s[:i]+s[i+1:])]
    return result


perms('abc')

['abc', 'acb', 'bac', 'bca', 'cab', 'cba']


print('\n'.join(perms('abc')))

abc
acb
bac
bca
cab
cba
karakfa
  • 66,216
  • 7
  • 41
  • 56
  • 1
    Thanks @karakfa, this is the cleanest and easiest to understand recursive permutation implementation I've seen for python! – Jay Taylor Jun 29 '16 at 00:07
13

There you go (recursive permutation):

def Permute(string):
    if len(string) == 0:
        return ['']
    prevList = Permute(string[1:len(string)])
    nextList = []
    for i in range(0,len(prevList)):
        for j in range(0,len(string)):
            newString = prevList[i][0:j]+string[0]+prevList[i][j:len(string)-1]
            if newString not in nextList:
                nextList.append(newString)
    return nextList

In order to get a list of all permutation strings, simply call the function above with your input string. For example,

stringList = Permute('abc')

In order to get a single string of all permutation strings separated by new-line characters, simply call '\n'.join with the output of that function. For example,

string = '\n'.join(Permute('abc'))

By the way, the print results for the two options above are identical.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
barak manos
  • 29,648
  • 10
  • 62
  • 114
  • I am looking to return a string, not a list. Can you see where in my code I am going wrong? – gnp210 Apr 16 '14 at 19:16
  • @gnu210. You are looking for all the permuations. That means you are looking to return *lots of of strings*, not just a string. – Adrian Ratnapala Apr 16 '14 at 19:31
  • @gnp210: Is this for real??? The desired result in **your own question** is a list of strings!!! If you want to get all permutations of a string, then what you get is a list of strings, **not** a single string!!! – barak manos Apr 16 '14 at 19:32
  • Yes, this is for real. I am able to solve with using a list. I am trying to return a string with '\n' separating each permutation. Again, this is not for practical purposes, but to understand string manipulation and recursiveness, with regards to a permutation problem. – gnp210 Apr 16 '14 at 19:37
  • 1
    @gnp210: Oh, OK, I get it. Then you should simply call `'\n'.join(Permute('abc'))`. I will add it to the answer. – barak manos Apr 16 '14 at 19:43
  • Thanks for providing this, but it is not what I am looking for. I know what I am asking doesn't make much sense from a practicality standpoint, but I am seeking a way to print the desired results directly from the function. I am starting to think this may be impossible with a recursive function. – gnp210 Apr 16 '14 at 21:20
  • 1
    @gnp210: A recursive function "tends" to perform the same thing many times, due to its nature of calling itself. Now, since it builds up the final output from partial outputs, the final output is only available to you **outside** the function (where you call it). Assuming that you **do not** want to print all the partial outputs that are generated during the process, you should call `print` **only outside** the recursive function!!! – barak manos Apr 16 '14 at 21:24
  • 1
    Suppose I have the str = 'h'. prevList returns []. nextList returns []. Both have length 0. Therefore, the permutation set returns []. – Jossie Calderon Jun 21 '16 at 00:10
  • @JossieCalderon: Have you actually tested this claim of yours??? `Permute('h')` returns `[h]`!!!. Thanks a lot for down-voting me on this false claim! – barak manos Jun 21 '16 at 06:13
  • @barakmanos Would you be kind enough to explain how this works for case `str = 'h'`? – Jossie Calderon Jun 21 '16 at 06:20
  • @JossieCalderon: Would you be kind enough to run it in your favorite Python IDE? The same way it works for any other string. – barak manos Jun 21 '16 at 06:26
  • @Marko Mackic: Your suggested edit (`if len(string) == 1: return [string]`) in unnecessary. The code works fine on single-character strings without this supplemental. Please feel free to give it a try. – barak manos Jun 21 '16 at 06:28
  • @JossieCalderon: In short, your claim that `prevList` is `[]` with length = 0 is wrong. `prevList` is `['']` with length = 1. If you can't see this by reading the code, then you could simply run it in a debugger step by step and have it verified. – barak manos Jun 21 '16 at 06:38
  • Mistakes were made. – Jossie Calderon Jun 21 '16 at 10:02
  • @JossieCalderon: Mistakes can happen, but I suggest checking beforehand, in particularly if it's **easy to do so**. Also, it seems that your comment has lead to a couple of down-votes (one of them possibly yours?), as well as an unnecessary edit attempt. This attempt was eventually rejected, but it was clearly trying to implement your suggestion (see my comment to Marko Mackic above). – barak manos Jun 21 '16 at 10:37
  • There's not much I can do about that. – Jossie Calderon Jun 21 '16 at 10:41
  • 1
    Meta discussion about this answer: http://meta.stackoverflow.com/questions/326544/people-upvoting-an-answer-without-checking-its-correctness-python – user000001 Jun 21 '16 at 13:06
  • 1
    @user000001: Thank you very much for pointing this out. Not only did that user make this false comment about my answer (see comment thread above), but he also posted that false comment, excusing his action, on this other forum. I will make sure to add this comment thread there too. Thanks again :) – barak manos Jun 21 '16 at 13:25
  • @Jossie Calderon: You could **by the least** revoke your own down-vote and admit the mistake (also in your post at meta stack overflow). – barak manos Jun 21 '16 at 13:49
  • "You last voted on this answer 13 hours ago. Your vote is now locked in unless this answer is edited." – Jossie Calderon Jun 21 '16 at 14:04
  • @JossieCalderon: Oh, OK, I will need to edit the answer in order to allow you to change your vote, and I don't have any good reason for that, so I suppose that admitting the mistake is good enough... – barak manos Jun 21 '16 at 14:49
  • http://stackoverflow.com/questions/23116911/all-permutations-of-a-string-in-python-recursive/23117430?noredirect=1#comment63331461_23117430 – Jossie Calderon Jun 21 '16 at 14:50
1

Here is the code:

def fperms(elements):
    if len(elements)<=1:
        yield elements
    else:
        for p in fperms(elements[1:]):
            for i in range(len(elements)):
                yield p[:i]+elements[0:1]+p[i:]
S.A.
  • 1,819
  • 1
  • 24
  • 39
apoorva R
  • 11
  • 3
0

Not sure about efficiency but this should work too.

def find_permutations(v):
    if len(v) > 1:
        for i in perms(v):
            nv = i[1:]
            for j in perms(nv):
                print(i[0] + j)
    else:
        print(v)


def perms(v):
    if not hasattr(perms, 'original'):
        perms.original = v
        perms.list = []
    nv = v[1:] + v[0]
    perms.list.append(nv)
    if perms.original == nv:
        l = perms.list
        del perms.original
        del perms.list
        return l
    return perms(nv)

find_permutations('abc')
0
def get_permutations(sequence):
    if len(sequence) == 1:
        return [sequence]  # base case
    else:
        result = []
        for letter in sequence:
            result += [letter +
                    other_letter for other_letter in get_permutations(sequence.replace(letter, ""))]


 test_1 = 'abc'
print("Input: ", test_1)
print("Expected output: ", ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])
print("Actual output: ", get_permutations(test_1))
Athena
  • 1
-3

This kind of thing is a nice place for generators (https://docs.python.org/3.3/tutorial/classes.html#generators), and yield.

Try something like this (not tested):

def permute_string(s):
    if len(s) <= 1:   #  "" and 1 char strings are their all their own permutaions.
        yield s
        return

    head = s[0] # we hold on to the first character
    for tail in permute_string(s[1:]) :   # permute the rest
        yield head + tail


# main
for permutation in permute_string(s) :
    print(permutation)

This is the classic permutation algorithm: you keep the first character and prepend it to all permutations of the remaining characters. This particular function is a python generator: that means it can keep running while yielding its results one-by-one. In this case, it makes it easier to concentrate on the algorithm without worrying about the details of how to get the data back to the caller.

Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
  • This one gives a list out of bound exception in the for-loop once s reaches a size of 1. I tried putting the for loop inside of an else statement, but then the function only returns "abc" – gnp210 Apr 16 '14 at 21:38
  • The bugs were that the (a) function tried to keep going even after yielding it's one-and-only result and (b) I used `< 1` when I meant `<= 1`. I have fixed both these problems in the example. – Adrian Ratnapala Apr 17 '14 at 06:31