Search code examples
recursionpermutation

Finding all possible permutations of the characters in a set of strings using recursion


I have this set of (Greek) strings:

ἸἼΙἹἽ, ῇηἤήῃὴῆἡἠἢᾖἥἣῄἦᾗᾐἧᾔᾑ, σς, οὸόὀὄὅὂ, ὺὖυῦύὐὑὔΰϋὕὗὓὒῢ

I'd like to find all possible permutations of the characters in these 5 strings. For example, Ἰῇσοὺ, Ἰῇσοὖ, Ἰῇσου, etc. I know it should involve recursion since the number of strings is not fixed but I'm a beginner and I'm completely dumbfounded by recursion.

I did the following in Python and it does give me all combinations of the characters in each string. But I need the 'ἸἼΙἹἽ' to always come first, 'ῇηἤήῃὴῆἡἠἢᾖἥἣῄἦᾗᾐἧᾔᾑ' second,'σς' third, etc.

# -*- coding: utf-8 -*-

def Gen( wd, pos, chars ):
    if pos < len( chars ):
        for c in chars:
            for l in c:
                Gen( wd + l, pos + 1, chars )
    else:
        print wd

chars = [ u'ἸἼΙἹἽ', u'ῇηἤήῃὴῆἡἠἢᾖἥἣῄἦᾗᾐἧᾔᾑ', u'σς', u'οὸόὀὄὅὂ', u'ὺὖυῦύὐὑὔΰϋὕὗὓὒῢ' ]

Gen( "", 0, chars )

Thanks for the help everybody. My mind is completely blown. Recursion! Here's what I ended up doing in Python:

# -*- coding: utf-8 -*-

s = [ u'ἸἼΙἹἽ', u'ῇηἤήῃὴῆἡἠἢᾖἥἣῄἦᾗᾐἧᾔᾑ', u'σς', u'οὸόὀὄὅὂ', u'ὺὖυῦύὐὑὔΰϋὕὗὓὒῢ' ]

results = []

def recur( wd, strings ):
    index = 0
    if index < len( strings ):
        for c in strings[ index ]:
            recur( wd + c, strings[ index + 1: ] )
    else:
        results.append( wd )

def main():
    recur( '', s )
    for r in results:
        print r.encode( 'utf-8' )

main()

Solution

  • Just write down the five nested loops. In pseudocode,

    for a in "ἸἼΙἹἽ"
      for b in "ῇηἤήῃὴῆἡἠἢᾖἥἣῄἦᾗᾐἧᾔᾑ"
        for c in "σς"
          for d in "οὸόὀὄὅὂ"
            for e in "ὺὖυῦύὐὑὔΰϋὕὗὓὒῢ"
               emit [a,b,c,d,e]
    

    To encode these nested loops with recursion, so it's good for any number of input strings, again in pseudocode,

    g(list-of-strings) =
       | case list-of-strings
       | of  empty --> end-of-processing
       | of  (first-string AND rest-of-strings) --> 
                for each ch in first-string 
                   DO g(rest-of-strings)
    

    Now you only need to figure out where to hold each current first-string's character ch and how to combine them all while at the end-of-processing (basically, your two options are a global accumulator, or an argument to a function invocation).

    The latter approach can be coded as

    g( picks, list-of-strings) =   # initially, picks = []
       | case list-of-strings
       | of  empty --> end-of-processing( picks )
       | of  (first-string AND rest-of-strings) --> 
                for each ch in first-string 
                   DO g( [...picks, ch], rest-of-strings )