简体   繁体   中英

Generating all possible combinations from a list of lists

I have the following lists:

[[a,b,c],[b],[d,a,b,e],[a,c]]

This list represents a mini-world in a puzzle problem. In this example the world contains 4 piles of objects stacked on-top of each other. I can only move the top object and place it on-top of some other stack. The letters represent a type of object, for example a might be a rock and ba ball. I need to generate all possible states that can exist for this mini-world. Im trying to do this in python but don't know how to implement it.

You can use itertools.combinations_with_replacement with list comprehension:

import itertools
ll = [['a','b','c'],['b'],['d','a','b','e'],['a','c']]
print [list(itertools.combinations_with_replacement(item,len(item))) for item in ll] 

It gives the combinations for each element in list of lists.

Output:

 [[('a', 'a', 'a'), ('a', 'a', 'b'), ('a', 'a', 'c'), ('a', 'b', 'b'), ('a', 'b', 'c'), ('a', 'c', 'c'), ('b', 'b', 'b'), ('b', 'b', 'c'), ('b', 'c', 'c'), ('c', 'c', 'c')], 
 [('b',)], 
 [('d', 'd', 'd', 'd'), ('d', 'd', 'd', 'a'), ('d', 'd', 'd', 'b'), ('d', 'd', 'd', 'e'), ('d', 'd', 'a', 'a'), ('d', 'd', 'a', 'b'), ('d', 'd', 'a', 'e'), ('d', 'd', 'b', 'b'), ('d', 'd', 'b', 'e'), ('d', 'd', 'e', 'e'), ('d', 'a', 'a', 'a'), ('d', 'a', 'a', 'b'), ('d', 'a', 'a', 'e'), ('d', 'a', 'b', 'b'), ('d', 'a', 'b', 'e'), ('d', 'a', 'e', 'e'), ('d', 'b', 'b', 'b'), ('d', 'b', 'b', 'e'), ('d', 'b', 'e', 'e'), ('d', 'e', 'e', 'e'), ('a', 'a', 'a', 'a'), ('a', 'a', 'a', 'b'), ('a', 'a', 'a', 'e'), ('a', 'a', 'b', 'b'), ('a', 'a', 'b', 'e'), ('a', 'a', 'e', 'e'), ('a', 'b', 'b', 'b'), ('a', 'b', 'b', 'e'), ('a', 'b', 'e', 'e'), ('a', 'e', 'e', 'e'), ('b', 'b', 'b', 'b'), ('b', 'b', 'b', 'e'), ('b', 'b', 'e', 'e'), ('b', 'e', 'e', 'e'), ('e', 'e', 'e', 'e')], 
 [('a', 'a'), ('a', 'c'), ('c', 'c')]]

If you would like to avoid duplicate same element in a combination, you can use itertools.permutation :

 [list(itertools.permutations(item,len(item))) for item in ll] 

Output:

[[('a', 'b', 'c'), ('a', 'c', 'b'), ('b', 'a', 'c'), ('b', 'c', 'a'), ('c', 'a', 'b'), ('c', 'b', 'a')], 
[('b',)], 
[('d', 'a', 'b', 'e'), ('d', 'a', 'e', 'b'), ('d', 'b', 'a', 'e'), ('d', 'b', 'e', 'a'), ('d', 'e', 'a', 'b'), ('d', 'e', 'b', 'a'), ('a', 'd', 'b', 'e'), ('a', 'd', 'e', 'b'), ('a', 'b', 'd', 'e'), ('a', 'b', 'e', 'd'), ('a', 'e', 'd', 'b'), ('a', 'e', 'b', 'd'), ('b', 'd', 'a', 'e'), ('b', 'd', 'e', 'a'), ('b', 'a', 'd', 'e'), ('b', 'a', 'e', 'd'), ('b', 'e', 'd', 'a'), ('b', 'e', 'a', 'd'), ('e', 'd', 'a', 'b'), ('e', 'd', 'b', 'a'), ('e', 'a', 'd', 'b'), ('e', 'a', 'b', 'd'), ('e', 'b', 'd', 'a'), ('e', 'b', 'a', 'd')], 
[('a', 'c'), ('c', 'a')]]

You can do this with a recursive generator. The logic works like this:

  • yield the current state
  • find all the states that are one move away from this state
  • recursively apply the logic to those

There is one problem with this: it is possible to come back to the starting point (move 'c' right one stack, then move 'c' left one stack), which would lead to an infinite recursion. I get around this by only considering moves in one direction, but the more general approach is to keep track of all the states you've seen already, and bail out if you arrive back at one.

This is what I have:

def shuffle_stacks(state):
    '''
    shuffle_stacks([[a], [b, c]]) -> 
       [[a, b], [c]], 
       [[a], [c, b]], 
       [[], [c, b, a]]

    yield all possible states that can be given by moving an item off
    the top of a sublist to the top of any other sublist, recursively.

    Currently only moves items to the right of their starting point.

    '''
    yield state

    # Find all the possible states we can be in if we move
    # one item off the top of a stack to any stack to its right
    # Apply recursively to each such state

    for i, source in enumerate(state):
        unchanged_state = state[:i]
        targets = state[i+1:]
        source = source[:]

        if not source or not targets:
            continue

            item = source.pop()
            for j, target in enumerate(targets):
                # Copy mutables to isolate changes
                target = target[:]
                targets = targets[:]
                targets[j] = target

                target.append(item)
                next_state = unchanged_state + [source] + targets
                yield from shuffle_stacks(next_state)

Note that yield from doesn't exist in Python 2.7, but if upgrading to at least 3.3 isn't an option for you, you can replace it with a loop:

for new_state in shuffle_stacks(next_state):
     yield new_state

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM