Generate all possible sequences of N elements with sequential rules - python

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.

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]

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), ...]

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.

Related

Split list into chunks with repeats between chunks

I have an array of elements, for example r = np.arange(15).
I'm trying to split this array into chunks of consecutive elements, where each chunk (except maybe the last one) has size M and there are m repeating elements between each pair of chunks.
For example: split_to_chunks(np.arange(15), M=5, m=1) should yield four lists:
[0, 1, 2, 3, 4], [4, 5, 6, 7, 8], [8, 9, 10, 11, 12], [12, 13, 14]
Obviously this can be done iteratively, but I'm looking for a more "pythonic" (and faster) way of doing this.
Something like this with list comprehension:
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
Example:
import math
l = list(range(15))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
m, M = 2, 5
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
# [[0, 1, 2, 3, 4],
# [3, 4, 5, 6, 7],
# [6, 7, 8, 9, 10],
# [9, 10, 11, 12, 13],
# [12, 13, 14]]
m, M = 3, 5
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
# [[0, 1, 2, 3, 4],
# [2, 3, 4, 5, 6],
# [4, 5, 6, 7, 8],
# [6, 7, 8, 9, 10],
# [8, 9, 10, 11, 12],
# [10, 11, 12, 13, 14]]
l = range(5)
m, M = 2, 3
[l[i*(M-m):i*(M-m)+M] for i in range(math.ceil((len(l)-m)/(M-m)))]
# [range(0, 3), range(1, 4), range(2, 5)]
Explanation:
Chunk i starts at index i*(M-m) and ends M positions later at index i*(M-m) + M.
chunk index starts ends
-------------------------------------------------
0 0 M
1 M-m M-m+M = 2*M-m
2 2*M-m-m=2(M-m) 2*(M-m)+M = 3M-2m
...
Now the problem is to determine how many chunks.
At each step we increase the initial index by M-m, so to count the total number of steps we need to divide the length of the list by M-m (but after subtracting m because in the first chunk we're not skipping anything).
Finally, use the ceiling function to add the last incomplete chunk in case the division is not exact.
This should do the job:
def split_to_chunks(r, M=5, m=1):
return [r[i*(M-m): (i+1)*M-i*m] for i in range(len(r)//(M-m)+1) if i*(M-m) < len(r)]
Explanation: in a list comprehension loop through the indexes in the way explained in the question. Each start of a chunk will start at i*(M-m) and end at (i+1)*M-i*m. Finally if the start of the chunk is after the length of the array it will skip it.

Finding all possible combinations of elements in a list of integers. all elements of any of the new list have to be at least 2 apart

I need a function that gets a list as an input and returns all the combinations with the maximum amount of integers used (here 5) which don't have 2 adjacent integers like 2, 3 or 6,7.
list0 = [0, 3, 4, 6, 10, 11, 12, 13]
all_combinations = magic_function(list0)
all_combinations would be this:
[[0, 3, 6, 10, 12],
[0, 3, 6, 11, 13],
[0, 4, 6, 10, 12],
[0, 4, 6, 11, 13]]
It could be done by getting all combinations and then picking out the correct ones, but I can't have it use much memory or be slow, because it has to work with lists with length up to 98 elements.
You can use a recursive generator function:
def combos(d, c = []):
if len(c) == 5:
yield c
else:
for i in d:
if not c or c[-1]+1 < i:
yield from combos(d, c+[i])
list0 = [0, 3, 4, 6, 10, 11, 12, 13]
print(list(combos(list0)))
Output:
[[0, 3, 6, 10, 12],
[0, 3, 6, 10, 13],
[0, 3, 6, 11, 13],
[0, 4, 6, 10, 12],
[0, 4, 6, 10, 13],
[0, 4, 6, 11, 13]]
My approach is as follows:
import itertools
lst = [0, 3, 4, 6, 10, 11, 12, 13] # 0 | 3 4 | 6 | 10 11 12 13
chunks, chunk = [], [] # defining chunk here is actually useless
prev = None
for x in lst:
if prev is None or x - prev > 1: # if jump > 1
chunks.append(chunk := []) # insert a brand-new chunk
chunk.append(x)
prev = x # update the previous number
def max_nonadjacents(chunk): # maximal nonadjacent sublists (given a chunk)
if not chunk or len(chunk) % 2: # odd length is easy
return {tuple(chunk[::2])}
return{tuple((chunk[:i] + chunk[i+1:])[::2]) for i in range(len(chunk))}
output = [list(itertools.chain.from_iterable(prod)) # flattening
for prod in itertools.product(*map(max_nonadjacents, chunks))]
print(output)
# [[0, 3, 6, 11, 13], [0, 3, 6, 10, 12], [0, 3, 6, 10, 13], [0, 4, 6, 11, 13], [0, 4, 6, 10, 12], [0, 4, 6, 10, 13]]
I am assuming that the input list is sorted.
Basically, my approach starts with recognizing that the problem can be divided into smaller pieces; the list can be divided into chunks, where each chunk comprises of running integers; [0], [3, 4], [6], [10, 11, 12, 13].
Then you can see you can get all the possible combinations by taking all the maximal non-adjacent lists from each chunk, and then taking the products of the lists across the chunks.
The code follows this procedure: (i) get the chunks, (ii) define a helper function max_nonadjacents that extracts all the maximal non-adjacent lists, (iii) apply it to each chunk (map(max_nonadjacents, ...)), and then (iv) take the products.

I need to make a dictionary from a list of lists [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have this:
A = [[0, 3, 6, 11], [0, 5, 7, 11], [0, 1, 2, 4, 8, 9, 10, 11]]
I want to make a diccionary from A like this:
dic_A = { 0: [0, 3, 6, 11], 1: [0, 5, 7, 11], 2: [0, 1, 2, 4, 8, 9, 10, 11]}
I have tried different ways and I can only use a for in range to solve the problem I think this can be reduced to a line somehow but I can't find the way
I tried this:
dic_A = {}
for i in range(3):
dic_A[i] = A[i]
You can use dictionary comprehension with enumerate to easily create the structure you desire. In my example the i (integer) will be the index of the child list and the d (data) will be the actual child list. However, there really doesn't seem much reason to do this, because the only syntactical difference will be from A[0] to B.0. You will actually have to jump through some hoops to target keys in B without possible errors, if you decide to use it in a ranged loop. The syntax to use it in a loop would become identical to A, as you would have to key B (ex: B[i]). You will also lose simple checks, like: len(A) to determine if the index is even available or to create a range based on what is known to be available. In short: You are potentially creating future problems for yourself by doing this. Whatever you are trying to accomplish with this, there is probably a much better way. If we are going to implement Occam's Razor, the "much better way" is more-than-likely to simply use the list.
A = [[0, 3, 6, 11], [0, 5, 7, 11], [0, 1, 2, 4, 8, 9, 10, 11]]
B = {i:d for i, d in enumerate(A)}
print(B) #{0: [0, 3, 6, 11], 1: [0, 5, 7, 11], 2: [0, 1, 2, 4, 8, 9, 10, 11]}
This should work:
dic_A = {index: array for index, array in enumerate(A)}
A = [[0, 3, 6, 11], [0, 5, 7, 11], [0, 1, 2, 4, 8, 9, 10, 11]]
# Create a dictionary
dic = {}
# loop through A and assign Key, value pairs based on list index
for index, value in enumerate(A):
dic[index] = value
You can it with a comprehension like this:
A = [[0, 3, 6, 11], [0, 5, 7, 11], [0, 1, 2, 4, 8, 9, 10, 11]]
{x: y for x, y in enumerate(A)}
Output:
{0: [0, 3, 6, 11], 1: [0, 5, 7, 11], 2: [0, 1, 2, 4, 8, 9, 10, 11]}
You can use enumerate() inbuilt function
A = [[0, 3, 6, 11], [0, 5, 7, 11], [0, 1, 2, 4, 8, 9, 10, 11]]
d = dict()
for i,el in enumerate(A):
d[i] = el
print(d)

Generate a 2D array with counter using list comprehension

I am new to python and learning more about list comprehensions. I want to generate a simple 2D array like this:
Expected List:
[[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]
What I have tried:
[[j for j in range(6)] for _ in range(6)]
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
Maybe i need some kind of counter instead of j inside the second loop, however doing count += 1 is not allowed here and will give a syntax error.
Simple solution using range(start, stop[, step]) function with step argument:
result = [list(range(i, i+4)) for i in range(1, 16, 4)]
print(result)
The output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
For a positive step, the contents of a range r are determined by the
formula r[i] = start + step*i where i >= 0 and r[i] < stop.
[[a for a in range(1+(4*b), 5+(4*b))] for b in range(0, 4)]
this uses the same list comprehension.
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Using Nested list comprehensions :
[[j+i*4 for j in range(1,5)] for i in range(4)]
Output :
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Do your derivation by creating a for loop:
l = []
n = 4
for i in range(3):
l2 = [j + (n*i) for j in range(n)]
l.append(l2)
Then convert that to a list comprehension:
x = [[j + (n*i) for j in range(n)] for i in range(3)]
Output:
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
You may also use get_chunks() function of utilspie library:
>>> from utilspie.iterutils import get_chunks
>>> list(get_chunks(range(1, 17), 4))
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Note: utilspie is not a built-in libary. You have to install it explicitly from pip via doing:
sudo pip install utilspie

constucting a matrix with lists of lists in Python [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 6 years ago.
I try to construct a matrix with a lists of lists in Python3.
When I implement the following function,
def myfunction(h, l):
resultmat = (h+1) * [(l+1) * [0]]
for i in range(1,h):
for j in range(1, l+1):
resultmat[i][j] = i + j
return resultmat
I get the following result:
myfunction(2,3)
Out[42]: [[0, 2, 3, 4], [0, 2, 3, 4], [0, 2, 3, 4]]
I had expected that the first rows and columns would be filled with zeroes, and that all other elements would be the sum of the row and column index. This is not exactly what I get here, where each row is a copy of the others. Can someone explain me (a) what is going on (b) how I can solve this problem?
The problem is the list multiplication. You aren't getting four lists, you're getting one list four times. Use
resultmat = [[0 for i in range(h+1)] for j in range(l+1)]
to build your zero-list instead.
Edit:
Reading the rest of your question, you could probably do the whole thing in a list comprehension
[[i+j if i and j else 0 for i in range(h+1)] for j in range(l+1)]
When you created the initial zero-matrix:
resultmat = (h+1) * [(l+1) * [0]]
This creates a list of lists of zeros. But, the lists of zeros (the rows) are all a reference to the same list. When you change one, it changes all the others:
>>> l = 3*[3*[0]]
>>> l
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> l[0][0] = 1
>>> l
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]
>>>
When I changed the first list to contain a 1, all the lists now contain the 1 because they're all actually the same list.
While Patrick's answer is correct, here is a slightly more readable version of your code, which accomplishes what you want. It creates a matrix for which every cell is the sum of the two indices, and then zeros the first row and the first column.
from pprint import pprint
def create_matrix(height, length):
matrix = [ [ i + j for j in range(length) ] for i in range(height) ]
matrix[0] = length*[0] # zero the first row
for row in matrix:
row[0] = 0 # zero the first column
return matrix
pprint(create_matrix(10, 11))
Output:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
[0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
[0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
[0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
[0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[0, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
[0, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
[0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
[0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]]

Categories