-1

I want to make a recursive function called makeCombinations that will take in an input a string and return all possible combinations of its letters as a list. So far I have this:

def delist(l):
    '''delist takes in input list l and if it its a nested list returns a new list that de-nests all the lists. Ex: [1,2,[3,4]] -> [1,2,3,4]'''
    if(l == []):
        return []
    else:
        if(isinstance(l[0], list)):
            return delist(l[0]) + delist(l[1:])
        else:
            return [l[0]] + delist(l[1:])

 
def makeCombinations(word, currentString=''):
    if(word == ''):
        return currentString
    else:
        use_it = makeCombinations(word[1:], currentString = currentString + word[0])
        lose_it = makeCombinations(word[1:], currentString)
        return delist([use_it , lose_it])
print(makeCombinations("asmtp"))

When running makeCombinations it returns:

['asmtp', 'asmt', 'asmp', 'asm', 'astp', 'ast', 'asp', 'as', 'amtp', 'amt', 'amp', 'am', 'atp', 'at', 'ap', 'a', 'smtp', 'smt', 'smp', 'sm', 'stp', 'st', 'sp', 's', 'mtp', 'mt', 'mp', 'm', 'tp', 't', 'p', '']

Which is pretty close, however it isn't all, and I am actually looking for very specific words, "a", "at", "am", but also, "spam" which for some reason just wont be outputted. I feel like this is due to the fact I am only taking the right side and not checking the reverse case, but I am not sure how to do this. I can't use loops or itertools.

Cr3 D
  • 153
  • 6
  • From your example it looks like you want "all combinations of the letters and of all subsets of the letters". Is that why there are combinations of all lengths? – Bill Sep 30 '22 at 00:41
  • 2
    Also, you haven't specified whether order is important. I assume it is since you want "spam" to be returned even though "asmp" has been. Can you be more explicit about the rules you want the algorithm to implement? – Bill Sep 30 '22 at 00:43
  • @Bill Yes apologies for the miscommunication, I wanted those combinations to be returned as list items – Cr3 D Sep 30 '22 at 00:53
  • Order is not important I just need them all in any order – Cr3 D Sep 30 '22 at 00:54
  • 2
    If order is not important, why do you expect both "asmp" and "spam" to be returned? – Bill Sep 30 '22 at 00:56
  • @Bill because it is the main combination I am looking for, Example: I call makeCombinations("astmp"), one of the combinations that can be made from these letters is "spam", therefore it should be within the list of combinations made, or since m, a, and p are also in the string, the word "map" should also be a possible combination. – Cr3 D Sep 30 '22 at 00:58
  • By order I just mean order of the items printed – Cr3 D Sep 30 '22 at 00:59
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248458/discussion-between-bill-and-cr3-d). – Bill Sep 30 '22 at 01:02
  • Style notes: in Python you don't need parens around the condition for an `if`. Also, rather than nested conditionals the way you are using them, you could just use `elif`. – Chris Sep 30 '22 at 01:03
  • 1
    You could use `more_itertools.distinct_permutations`. – Kelly Bundy Sep 30 '22 at 01:04
  • The question as asked does not make any sense. I linked to a duplicate for the general techniques, even though the question already illustrates one of them (in a needlessly awkward way; rather than building a 2-element list of lists and flattening it, just **concatenate the lists** with `+`). The problem is that *the algorithm you want to use is not expected to create the result you describe*. The code cannot read your mind to know that "spam" is the ordering you want for the {a, s, m, p} subset. If you want all orderings for each subset, you must use additional logic to get them. – Karl Knechtel Sep 30 '22 at 18:07
  • "I can't use loops or itertools." What does "can't" mean? If this is for an assignment, please read https://meta.stackoverflow.com/questions/334822, and also make sure you understand the **exact** requirements. For example, you should be able to tell us, offhand, exactly **how many** results should be in the output for `"asmtp"` input, and also be able to explain what happens with duplicate letters in the input, e.g. `"foo"`. Most importantly, if there is something you don't understand about *what you are expected to do* for the assignment, please **ask your instructor**. – Karl Knechtel Sep 30 '22 at 18:09

2 Answers2

1

I think the use-it, lose-it algorithm is for finding all combinations (i.e. unique sets of letters). Because you say the order of the letters in the word is important, I think you are looking for all permutations (i.e. unique words).

Here's how you would find all combinations:

def makeCombinations(word, currentString=''):
    if(word == ''):
        return [currentString]
    use_it = makeCombinations(word[1:], currentString=currentString + word[0])
    lose_it = makeCombinations(word[1:], currentString)
    return use_it + lose_it

print(makeCombinations("abc"))
['abc', 'ab', 'ac', 'a', 'bc', 'b', 'c', '']

You could write a makePermutations(word) function and call that on each combination result:

def flatten_list(l):
    return [item for sublist in l for item in sublist]

def makePermutations(word, currentString=''):
    if(word == ''):
        return [currentString]
    return flatten_list([
        makePermutations(word.replace(a, ''), currentString=currentString + a)
        for a in word
    ])

assert(makePermutations("abc") == ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])

def makeAllPermutations(word, currentString=''):
    if(word == ''):
        return makePermutations(currentString)
    use_it = makeAllPermutations(word[1:], currentString=currentString + word[0])
    lose_it = makeAllPermutations(word[1:], currentString)
    return use_it + lose_it

print(makeAllPermutations("abc"))
['abc', 'acb', 'bac', 'bca', 'cab', 'cba', 'ab', 'ba', 'ac', 'ca', 'a', 'bc', 'cb', 'b', 'c', '']

However I used a list comprehension here so you'd have to add another level of recursion to eliminate that.

I think you might be able to combine makePermutations and makeAllPermutations functions into one but not sure.

Bill
  • 10,323
  • 10
  • 62
  • 85
-1

Is this what you want?

from itertools import permutations

def makeCombinations(word):
    return [''.join(p) for n in range(len(word))
            for p in permutations(word, n + 1)]

print(makeCombinations("asmtp"))


['a', 's', 'm', 't', 'p', 'as', 'sa', 'am', 'ma', 'at', 'ta', 'ap', 'pa', 'sm', 'ms', 'st', 'ts', 'sp', 'ps', 'mt', 'tm', 'mp', 'pm', 'tp', 'pt', 
    ... 'ptsma', 'ptmas', 'ptmsa']

Sorry it's not recursive.

Bill
  • 10,323
  • 10
  • 62
  • 85
  • I think I've overcomplicated it. `permutations(word, r)` returns all permutations of all combinations of length r. I will update the code. – Bill Sep 30 '22 at 01:34
  • @KellyBundy has a good point. If the original word has duplicate letters, some of the results will not be unique. As they suggested, use `more_itertools.distinct_permutations` not `permutations` if the input words may contain duplicate letters. – Bill Sep 30 '22 at 01:39
  • My second point with that, though, is that ruling out `itertools` (which they did!) is probably not enough. – Kelly Bundy Sep 30 '22 at 01:42
  • Ah, thanks I missed that: "no loops or itertools". – Bill Sep 30 '22 at 01:43