Michael Scott
Michael Scott

Reputation: 589

Python Calculating Unique List Permutation Possibilities

So I have a problem dealing with permutations of lists/strings, which I am having a hard time solving.

So, say I have several Lists:

list1 = ["a"]
list2 = ["a","b","c","d"]
list3 = ["b","e"]
list4 = ["f","g","a"]

I need to calculate the number of all possible combinations of permutations while choosing 1 character from each list. So, from the first list, I choose a character. "a", in this case since there is only one item in the list. Next I select an item from the second list, but it CAN'T BE "a", as that was chosen in my previous list, so it could be "b", "c", or "d". Next I choose an item from the third list, and if I chose "a" in the first, and "b", in the second, I could only choose "e", as "b" was already used previously. The same goes for the fourth list.

So I need to calculate all of the possible combinations of unique character combinations from my lists. Hopefully everyone gets what I'm asking here. Or if possible, I don't even need to create the lists of permutations, I just need to calculate HOW MANY combinations there are total. Whatever would be less memory intensive as there may be a large number of individual lists in the actual problem

To be more verbose with my question... If I had two lists: list1 = ["a"] list2 = ["b"]

There would only be one combination, as you preserve the location in the permuted strings. List one does not contain a b, so the only combination could be ("a","b"), not ("b","a"). And to further extends the constraints of this question .. I don't necessarily want to retrieve the results of all the permutations, I want to only return the TOTAL NUMBER of possible permutations. Returning the results takes up too much memory, as I will be working with rougly fifteen lists, of 1 to 15 characters in each list.

Upvotes: 2

Views: 978

Answers (3)

Paul Hankin
Paul Hankin

Reputation: 58399

You can cache counts of the form "starting from the i'th list, excluding elements in S". By being careful to limit S to only characters that may be excluded (that is, only elements that appear in a later list), you can reduce the amount of repeated computation.

Here's an example program:

def count_uniq_combs(sets, i, excluding, cache):
    if i == len(sets): return 1
    key = (i, excluding)
    if key in cache:
        return cache[key]
    count = 0
    for c in sets[i][0]:
        if c in excluding: continue
        newx = (excluding | set([c])) & sets[i][1]
        count += count_uniq_combs(sets, i + 1, newx, cache)
    cache[key] = count
    print key, count
    return count

def count(xs):
    sets = [[set(x)] for x in xs]
    # Pre-compute the union of all subsequent sets.
    union = set()
    for s in reversed(sets):
        s.append(union)
        union = union | s[0]
    return count_uniq_combs(sets, 0, frozenset(), dict())

print count(['a', 'abcd', 'be', 'fga'])

It prints out the values it's actually calculating (rather than recalling from the cache), which looks like this:

(3, frozenset(['a'])) 2
(2, frozenset(['a'])) 4
(2, frozenset(['a', 'b'])) 2
(1, frozenset(['a'])) 10
(0, frozenset([])) 10

For example, when looking at list 2 ("b", "e") there's only two counts computed: one where "a" and "b" are both excluded, and one where only "a" is excluded. Compare this to the naive implementation where you'd also be counting many other combinations (for example: "a" and "c").

If still isn't fast enough, you can try heuristics for sorting the lists: you want lists which contain relatively few symbols of other lists to come later.

Upvotes: 1

Carsten
Carsten

Reputation: 18446

Use itertools.product to generate all possible combinations from the lists. Then, using itertools.ifilter, filter out all combinations that contain a repeated character. One simple way to do this is to check if the length of the list stays the same if you remove all duplicates (i.e. if you create a set from it).

import itertools

list1 = ["a"]
list2 = ["a","b","c","d"]
list3 = ["b","e"]
list4 = ["f","g","a"]

f = lambda x: len(x) == len(set(x))
it = itertools.ifilter(f, itertools.product(list1, list2, list3, list4))

# print all combinations
for combination in it:
    print combination

Upvotes: 2

Mark Tolonen
Mark Tolonen

Reputation: 178264

Use itertools.product. It iterates through all permutations of choosing one item for each list. Additionally, use a list comprehension to eliminate the iterations that don't meet your requirements.

>>> a='a'
>>> b='abcd'
>>> c='be'
>>> d='fga'
>>> import itertools
>>> [a+b+c+d for a,b,c,d in itertools.product(a,b,c,d) if b != a and c not in [a,b] and d not in [a,b,c]]
['abef', 'abeg', 'acbf', 'acbg', 'acef', 'aceg', 'adbf', 'adbg', 'adef', 'adeg']

Upvotes: 1

Related Questions