Generate all possible sequences of N elements with sequential rules

Question:

I have a function get_appendable_values(sequence) that takes a sequence (even empty) and returns a list of all the values appendable (as a last element) to that sequence. I need to generate all the possible sequences of 4 elements, with respect to the rules defined in this function and starting with an empty sequence.

Example :

Let’s say the implementation of get_appendable_values is :

def get_appendable_values(sequence):
    '''Dummy rules'''
    if len(sequence) == 2:
        return [4, 12]
    if sequence[-1] == 4:
        return [7]
    return [0, 9]

Expected output :

[[0, 0, 4, 7],
[0, 0, 12, 0],
[0, 0, 12, 9],
[0, 9, 4, 7],
[0, 9, 12, 0],
[0, 9, 12, 9],
[9, 0, 4, 7],
[9, 0, 12, 0],
[9, 0, 12, 9],
[9, 9, 4, 7],
[9, 9, 12, 0],
[9, 9, 12, 9]]

I have the feeling that recursion is the key, but I could not figure it out.

Asked By: Betcha

||

Answers:

I’m not sure if I understand your problem correctly. Do you want to get a list of the possible permutations of length 4, drawn from the sequence?

In that case, the itertools package might come in handy (see How do I generate all permutations of a list?):

import itertools

a = [2, 4, 6, 8, 10]

permutations_object = itertools.permutations(a, 4)

print(list( permutations_object ))

This outputs a list of tuples which are the permutations:

[(2, 4, 6, 8), (2, 4, 6, 10), (2, 4, 8, 6), (2, 4, 8, 10), …]

Answered By: Adrian Usler

Here’s a solution which works using recursion, although as Adrian Usler suggests, using the itertools library probably works better. I think the following code works:

def gen_all_sequences(sequence):
    # Recursive base case:
    if len(sequence) <= 1:
        return [sequence] # Only one possible sequence of length 1
    
    # Recursive general case:
    all_sequences = []
    for i, elem in enumerate(sequence):
        # Construct and append all possible sequences beginning with elem
        remaining_elements = sequence[:i]+sequence[i+1:] 
        all_sequences += [[elem]+seq for seq in gen_all_sequences(remaining_elements )]
    return all_sequences

print(gen_all_sequences(["a","b","c","d"]))
# Will return all arrangements (permutations) of "a", "b", "c" and "d", e.g. abcd, abdc, acbd, acdb etc.
Answered By: Aidan Wansbrough

Yes, recursion is the key. To generate a sequence of size 4, you first generate all sequences of size 3, and add all possible endings to them. Likewise, to generate a sequence of size 3, you need all sequences of size 2… and so forth down to size 0.

def get_appendable_values(sequence):
    '''Dummy rules'''
    if len(sequence) == 2:
        return [4, 12]
    #need a len check here to avoid IndexError when `sequence` is empty
    if len(sequence) > 0 and sequence[-1] == 4:
        return [7]
    return [0, 9]

def generate_sequences(size):
    if size == 0:
        yield []
    else:
        for left_part in generate_sequences(size-1):
            for right_part in get_appendable_values(left_part):
                yield left_part + [right_part]

for seq in generate_sequences(4):
    print(seq)

Result:

[0, 0, 4, 7]
[0, 0, 12, 0]
[0, 0, 12, 9]
[0, 9, 4, 7]
[0, 9, 12, 0]
[0, 9, 12, 9]
[9, 0, 4, 7]
[9, 0, 12, 0]
[9, 0, 12, 9]
[9, 9, 4, 7]
[9, 9, 12, 0]
[9, 9, 12, 9]
Answered By: Kevin
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.