1

My friend wrote a code to display all anagrams for "abcdef" in python. But in this code, I couldn't understand how the recursive procedure works anagrams = get_list_of_anagrams(''.join(tmp_list)) How does the function call itself?

def get_list_of_anagrams(s):
    if len(s)==0:
        return ['']
    all_chars = list(s)
    unique_chars = list(set(s))
    anagrams_list = []
    for char in unique_chars:
        tmp_list = list(all_chars)
        tmp_list.remove(char)
        anagrams = get_list_of_anagrams(''.join(tmp_list))
        for i in range(len(anagrams)):
            anagrams[i] = char+anagrams[i]
        anagrams_list += anagrams
    return anagrams_list

When I try to print every thing till anagrams = get_list_of_anagrams(''.join(tmp_list)

def get_list_of_anagrams(s):
    if len(s)==0:
        return ['']
    all_chars = list(s)
    unique_chars = list(set(s))
    anagrams_list = []
    for char in unique_chars:
        tmp_list = list(all_chars)
        tmp_list.remove(char)
        anagrams = get_list_of_anagrams(''.join(tmp_list))



print get_list_of_anagrams('abc')

I get the following out put.

enter image description here

For the following code:

def get_list_of_anagrams(s):
    if len(s)==0:
        return ['']
    all_chars = list(s)
    unique_chars = list(set(s))
    anagrams_list = []
    for char in unique_chars:
        tmp_list = list(all_chars)
        tmp_list.remove(char)
        anagrams = get_list_of_anagrams(''.join(tmp_list))
        print anagrams
        for i in range(len(anagrams)):
            anagrams[i] = char+anagrams[i]
        anagrams_list += anagrams
    return anagrams_list  

print get_list_of_anagrams('abc')

I get the following output:

enter image description here

can some one explain me why the above output is of this pattern?

dangerous
  • 171
  • 1
  • 1
  • 10
  • in your function , you get an empty list because it only `return['']` compared to your friend wrote `return ['']` and `return anagram_list `. to understanding a recursive function , try to find the exit (the `return`) and trace it from there – whale_steward Feb 26 '15 at 03:40
  • `for i in range(len(anagrams)):` But isnt the len(anagrams) empty?? . because When I print anagrams then I get a list of empty lists . Then how come in the code My friend has looped over len(anagrams), its just empty right? – dangerous Feb 26 '15 at 06:19
  • put this command `print anagrams` after `anagrams = get_list_of_anagrams(''.join(tmp_list))` in your friend code. it'll show you how recursion works – whale_steward Feb 26 '15 at 06:53
  • @whale_steward ok I will try this one – dangerous Feb 26 '15 at 08:38
  • @whale_steward - I tried your suggestion. but after each iteration I am getting an empty list [''] and then a word. This is the part I am confused how recursion is being implemented in this scenario. – dangerous Feb 27 '15 at 15:34

4 Answers4

2

Life is short use libraries:

import itertools
d = 'abc'
e = len(d)
j = list()

for p in itertools.permutations(d, e):
    print p

('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
MJP
  • 1,427
  • 2
  • 14
  • 20
1

Tracing through:

def get_list_of_anagrams(s):
    # base case - empty string, return empty
    if len(s)==0:
        return ['']

    # figure out which characters are available
    all_chars    = list(s)       # may have repeats
    unique_chars = list(set(s))  # no repeats

    # prepare to store all results
    anagrams_list = []

    # for each unique character
    for char in unique_chars:
        # remaining unused characters
        tmp_list = list(all_chars)
        tmp_list.remove(char)

        # recurse: get all anagrams of remaining characters
        anagrams = get_list_of_anagrams(''.join(tmp_list))

        # prefix each result with picked character
        for i in range(len(anagrams)):
            anagrams[i] = char+anagrams[i]

        # add to overall results
        anagrams_list += anagrams

    # return all results
    return anagrams_list

I would rewrite it a bit, like

def get_list_of_anagrams(s):
    anagrams = []

    remaining = list(s)
    for char in sorted(set(s)):
        remaining.remove(char)
        if remaining:
            # recurse: get all anagrams of remaining characters
            for anagram in get_list_of_anagrams(remaining):
                # prefix each result with picked character
                anagrams.append(char + anagram)
        else:
            anagrams.append(char)
        remaining.append(char)

    # return all results
    return anagrams

So to find get_list_of_anagrams("abcdef"), you get

"a" + each(get_list_of_anagrams("bcdef"))
"b" + each(get_list_of_anagrams("acdef"))
"c" + each(get_list_of_anagrams("abdef"))
"d" + each(get_list_of_anagrams("abcef"))
"e" + each(get_list_of_anagrams("abcdf"))
"f" + each(get_list_of_anagrams("abcde"))

etc.

Hugh Bothwell
  • 53,141
  • 7
  • 81
  • 98
  • I understood everything except the recursion part. for eg, in fibonacci The recursion is explained (https://users.soe.ucsc.edu/~fire/dev-2008f-12/labs/lab8-Recursion-vs-Iteration/fib_5.png) . is there a similar logic for the recursive function in this problem as well? – dangerous Feb 26 '15 at 03:51
  • Your solution is great up votes :) . Is it possible to write this function without using recursion.I know this is off topic, but it would be great if you could help me with this. – dangerous Feb 26 '15 at 06:23
  • I think it could be written as an iterative algorithm - Knuth's permutation algorithm with slight modifications to prevent same-letter repeats (ie "abcb" should only be returned once). Actually Knuth probably includes exactly that, you'd have to check. Basically it involves repeatedly rearranging a list of the letters according to a set of rules - see the source of `itertools.permutations` for an example. It is quite a bit faster (but also significantly harder to understand) than the recursive solution. – Hugh Bothwell Feb 26 '15 at 13:52
  • Ok sure I will have a look at the itertools.permutations. – dangerous Feb 27 '15 at 15:04
  • after each iteration I am getting an empty list [''] and then a word. This is the part I am confused how recursion is being implemented in this scenario. could you explain this thing to me? – dangerous Feb 28 '15 at 01:17
0

It's a Recursive function. For example the famous Fibonacci Sequence:

def fib(n):
    if n < 2:
        return n
    return fib(n-2) + fib(n-1)
print fib(10)

Check this question about recursive for more information.

Community
  • 1
  • 1
Aaron
  • 2,373
  • 3
  • 21
  • 52
  • I know it is a recursive function. But I dont understand how it is being applied in this context. – dangerous Feb 26 '15 at 03:21
  • 2
    I am tempted to change the link to http://stackoverflow.com/questions/28733761/write-a-program-to-display-all-the-anagrams-for-abcdef-in-python to make it a truly recursive definition ;-) – Hugh Bothwell Feb 26 '15 at 03:43
0

The idea is, to find the anagrams of 'abc', first find all the anagrams of 'bc', which is done by first finding all anagrams of 'c', which is done by first finding all anagrams of ''.

So how do you find the anagrams of the "outer layer" given all the anagrams of the "inner layer"? Given all the anagrams of 'bc' (which are 'bc' and 'cb'), you can find all the anagrams of 'abc' by inserting a into each positions of 'bc' and 'cb'. In details:

# 'bc' will give these anagrams:
abc # insert 'a' at first position
bac # insert 'a' at second position
bca # insert 'a' at third position (also last)

# Similarly, 'cb' gives:
acb
cab
cba

So 2 anagrams of 'bc' give you 6 anagrams of 'abc'.

===

About your algorithm: it removes 1 character at each recursion (using tmp_list.remove(char)). At the base case where len(s)==0 (i.e. when all characters have been removed), it simply returns ''. Then proceeds to find the anagrams of 'f' using the logic above, then the anagrams of 'ef'.... and finally returns you the anagrams of abcdef.

HuN
  • 109
  • 2
  • 8
  • Hi Huy, I have updated the question. I was able to understand the porgram 50 % through your explanation. But I couldnt understand why am I getting a pattern like that. – dangerous Feb 27 '15 at 15:27