all = []
def generate(i, current):
if i < 11:
current.append(i)
all.append(current)
i+= 1
generate(i, current)
generate(1, [])
print(all)
I want this function to generate
[[1], [1, 2]...[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
instead of
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]],
but don't know how to fix it.
Do you know the solution?
Here's my go:
def listGen(start, stop):
res = []
for i in range(start, stop+1):
res.append([x for x in range(start, i+1)])
return res
You could also simplify this to:
def listGen(start, stop):
return [[x for x in range(start, i+1)] for i in range(start, stop+1)]
Input: print(listGen(1, 10))
Output: [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
def generate_array():
result = []
for i in range(1, 11):
current_array = []
for j in range(1, i + 1):
current_array.append(j)
result.append(current_array)
return result
print(generate_array())
The code uses two nested for loops, where the outer loop iterates over range(1, 11) and the inner loop iterates over range(1, i + 1). The values of i and j are used to generate the sublists and append them to the result list, which is returned at the end of the function.
The core issue you have is that when you do:
all.append(current)
current is the exact same list all over the place so when you append to it in the prior line you effectively append to it everywhere. To fix that and the lightest change to your code you would append to copy of it.:
all = []
def generate(i, current):
if i < 11:
current.append(i)
all.append(current.copy()) ## <--- append a copy
i+= 1
generate(i, current)
generate(1, [])
print(all)
alternatively you could pass a copy like:
all = []
def generate(i, current):
if i < 11:
current.append(i)
all.append(current)
i+= 1
generate(i, current.copy()) ## <--- pass a copy
generate(1, [])
print(all)
In either case, the important part is that we get a distinct current to work with.
Note that the use of all as a variable clobbers the function all() and you might not want to do that. As I'm sure lots of others will point out, there are many ways to skin this cat.
I am currently trying to find all possible sets of a list of numbers where two or more elements in the set can NOT be in the same set.
For example, I have an original list [1, 2, 3, 4, 5, 6] and I have a list of sets [{1,2}, {3,4}], meaning that 1 and 2 can not be in the same set and 3 and 4 can not be in the same set.
Given those two inputs, result of the program should be:
{1, 6, 3, 5}
{1, 6, 4, 5}
{2, 6, 3, 5}
{2, 6, 4, 5}
Order does not matter in the final output.
Edit: I rewrote the implementation (without recursion this time). Now I am getting an error that says that I can't remove something from the list because it is not there...
def schedules(overlaps, complete):
print(complete)
final = complete.copy()
print(final)
for sch in complete:
print(sch)
for over in overlaps:
if (over[0] in sch) and (over[1] in sch):
print("This is an overlap!!!")
final.remove(sch)
return final
Here is the error and output of the above code:
[(1, 2, 3, 4), (1, 2, 3, 5), (1, 2, 3, 6), (1, 2, 4, 5), (1, 2, 4,
6), (1, 2, 5, 6), (1, 3, 4, 5), (1, 3, 4, 6), (1, 3, 5, 6), (1, 4,
5, 6), (2, 3, 4, 5), (2, 3, 4, 6), (2, 3, 5, 6), (2, 4, 5, 6), (3,
4, 5, 6)]
[(1, 2, 3, 4), (1, 2, 3, 5), (1, 2, 3, 6), (1, 2, 4, 5), (1, 2, 4,
6), (1, 2, 5, 6), (1, 3, 4, 5), (1, 3, 4, 6), (1, 3, 5, 6), (1, 4,
5, 6), (2, 3, 4, 5), (2, 3, 4, 6), (2, 3, 5, 6), (2, 4, 5, 6), (3,
4, 5, 6)]
(1, 2, 3, 4)
This is an overlap!!!
This is an overlap!!!
Traceback (most recent call last):
File "schedule.py", line 24, in <module>
result = schedules(overlaps, list(comb))
File "schedule.py", line 19, in schedules
final.remove(sch)
ValueError: list.remove(x): x not in list
Edit: Adding a try, except block around final.remove(sch) removed the error but as noted in the comments below this code will NOT work if there are more then two elements in an overlap set. For example: if overlaps is now [{1,2}, {3,4,5}] the output should be:
{1,6,3}
{1,6,5}
{1,6,4}
{1,6,5}
{2,6,3}
{2,6,5}
{2,6,4}
{2,6,5}
Suggestion:
Start from the list of overlaps and create all permutations using only ever one element for each set.
Add to each of the resulting list the elements from you initial list that do not figure in the overlaps.
In python this basically boils down to 2 lines that work for an arbitrary number of sets of arbitrary length:
from itertools import product
init_list = [1, 2, 3, 4, 5, 6]
overlaps = [{1,2}, {3,4}]
# the 2 lines:
rest = tuple(el for el in init_list if not any(el in ol for ol in overlaps))
[unique + rest for unique in product(*overlaps) if all(u in init_list for u in unique)]
Out[7]: [(1, 3, 5, 6), (1, 4, 5, 6), (2, 3, 5, 6), (2, 4, 5, 6)]
This should work with an arbitrary number of sets of arbitrary length, removing just 1 element of each set at a time from the list. The two versions below produce the same result, with the second using a a single list comprehension, which, while concise, is a bit hard to read.
First it filters the list of sets to get only those that are subsets of the original list. Then it uses itertools.product to derive all combinations using 1 element from each set, then for each result removes those elements from the original list.
from itertools import product
l = [1, 2, 3, 4, 5, 6]
test_sets = [{1, 2}, {3, 4}]
result = []
subsets = [u for t in test_sets for u in combinations(t, 2) if set(u).issubset(set(l))]
for s in set(product(*subsets)) if len(test_sets) > 1 else subsets:
result.append({r for r in l if r not in s})
print(result)
Result
[{2, 4, 5, 6}, {2, 3, 5, 6}, {1, 4, 5, 6}, {1, 3, 5, 6}]
This code works:
def schedules(overlaps, complete):
final = complete.copy()
for sch in complete:
for over in overlaps:
if (over[0] in sch) and (over[1] in sch):
try:
final.remove(sch)
except:
continue
return final
The error in the question above was caused by me trying to remove the first list twice since it had two overlaps in it. I used a try, except block to get past it.
I wonder if there are better or more "pythonic" ways to do this. Let me know if you have any improvements!
This should do the trick:
from itertools import combinations # to fill the rest of the sequence
# define a recursive function
# it begins to fill the sequences with the mutually exclusive sets
# but also tries to skip them
# it tries to construct only valid combinations
# so it should also perform for larger sets of numbers
def create_sequences(start_sequence, to_length, all_numbers, mutual_exclusive_sets, collect_sequences_in=None):
# leave it up to the user if he want's to pass a list
# if not just create one but for recursive calls just
# reuse the list to avoid garbage collection
result= collect_sequences_in if collect_sequences_in is not None else list()
curr_len= len(start_sequence)
if curr_len == to_length:
# well this is just for safety
result.append(start_sequence)
return result
# create a working copy, so we can remove one item
# (one set of elements which are mutually exclusive)
# so we can pass it to the next recursion if needed
# without spoiling anything
mutual_exclusive_sets= list(mutual_exclusive_sets)
if len(mutual_exclusive_sets) > 0:
# there are mutually exclusive sets left, so grab
# one, during this method call we will just work
# with that set, adding 0 or one elements to the
# sequence and leaving the rest up to a subsequent
# call (if adding one element doesn't complete
# the sequence)
mutual_exclusive_set= mutual_exclusive_sets.pop()
for value in mutual_exclusive_set:
if value in start_sequence:
# that may not be (should only happen if mual_exculsive_sets overlap)
return result
# ok so now call the function with the same sequence
# after removing the one set (this is the case in which
# we don't take any value from the set --> in your example
# that wouldn't be necessary since it will not produce
# a complete sequence and skip this anyways ;-)
create_sequences(list(start_sequence), to_length, all_numbers, mutual_exclusive_sets, collect_sequences_in=result)
# now the case that we take exactly one element from the set
# and add it to the sequence
for value in mutual_exclusive_set:
work_sequence= start_sequence + [value]
if len(work_sequence) == to_length:
result.append(work_sequence)
else:
create_sequences(work_sequence, to_length, all_numbers, mutual_exclusive_sets, collect_sequences_in=result)
elif to_length - curr_len <= len(all_numbers):
# no mutual exclusive sets left, so now add from all_numbers
for tup in combinations(all_numbers, to_length - curr_len):
result.append(start_sequence + list(tup))
else:
# we would have to fill the sequence with items of all_numbers
# but there are no sufficient elements, so skip this step and
# leave result as it is (this was a dead-end --> like if we
# chose to skip one of the mutually exclusive sets in your example
# data --> but e.g. if you run the same with to_length=3 it is relevant)
pass
return result
for the following setup:
all_numbers= [1, 2, 3, 4, 5, 6]
mutual_exclusive= [{1, 2}, {3, 4}]
all_numbers_tmp= list(all_numbers)
for me in mutual_exclusive:
for n in me:
all_numbers_tmp.remove(n)
create_sequences([], 4, all_numbers_tmp, mutual_exclusive)
It returns:
Out[27]: [[3, 1, 5, 6], [3, 2, 5, 6], [4, 1, 5, 6], [4, 2, 5, 6]]
Here is a simpler way to have your desired output using combinations from itertools module along with all and any functions:
from itertools import combinations
def get_combs(ranges, forbidden, r):
for comb in combinations(ranges, r):
if not any(all(k in comb for k in elm) for elm in forbidden):
yield set(comb)
ranges = range(1, 7)
forbidden = [{1, 2}, {3, 4}]
r = 4
combs = list(get_combs(ranges, forbidden, r))
print(combs)
Output:
[{1, 3, 5, 6}, {1, 4, 5, 6}, {2, 3, 5, 6}, {2, 4, 5, 6}]
You can use recursion with a generator. This solution first finds all combinations of the desired length, and then filters based on the matching set:
data, d1 = [1, 2, 3, 4, 5, 6], [{1,2}, {3,4}]
l = len([i for b in d1 for i in b])
def _filter(_d):
return all(len(_d&c) < 2 for c in d1)
def combo(d, c = []):
if len(c) == l:
yield c
else:
for i in filter(lambda x:x not in c, d):
yield from combo(d, c+[i])
r = [i for i in combo(data) if _filter(set(i))]
Output:
[[1, 3, 5, 6], [1, 3, 6, 5], [1, 4, 5, 6], [1, 4, 6, 5], [1, 5, 3, 6], [1, 5, 4, 6], [1, 5, 6, 3], [1, 5, 6, 4], [1, 6, 3, 5], [1, 6, 4, 5], [1, 6, 5, 3], [1, 6, 5, 4], [2, 3, 5, 6], [2, 3, 6, 5], [2, 4, 5, 6], [2, 4, 6, 5], [2, 5, 3, 6], [2, 5, 4, 6], [2, 5, 6, 3], [2, 5, 6, 4], [2, 6, 3, 5], [2, 6, 4, 5], [2, 6, 5, 3], [2, 6, 5, 4], [3, 1, 5, 6], [3, 1, 6, 5], [3, 2, 5, 6], [3, 2, 6, 5], [3, 5, 1, 6], [3, 5, 2, 6], [3, 5, 6, 1], [3, 5, 6, 2], [3, 6, 1, 5], [3, 6, 2, 5], [3, 6, 5, 1], [3, 6, 5, 2], [4, 1, 5, 6], [4, 1, 6, 5], [4, 2, 5, 6], [4, 2, 6, 5], [4, 5, 1, 6], [4, 5, 2, 6], [4, 5, 6, 1], [4, 5, 6, 2], [4, 6, 1, 5], [4, 6, 2, 5], [4, 6, 5, 1], [4, 6, 5, 2], [5, 1, 3, 6], [5, 1, 4, 6], [5, 1, 6, 3], [5, 1, 6, 4], [5, 2, 3, 6], [5, 2, 4, 6], [5, 2, 6, 3], [5, 2, 6, 4], [5, 3, 1, 6], [5, 3, 2, 6], [5, 3, 6, 1], [5, 3, 6, 2], [5, 4, 1, 6], [5, 4, 2, 6], [5, 4, 6, 1], [5, 4, 6, 2], [5, 6, 1, 3], [5, 6, 1, 4], [5, 6, 2, 3], [5, 6, 2, 4], [5, 6, 3, 1], [5, 6, 3, 2], [5, 6, 4, 1], [5, 6, 4, 2], [6, 1, 3, 5], [6, 1, 4, 5], [6, 1, 5, 3], [6, 1, 5, 4], [6, 2, 3, 5], [6, 2, 4, 5], [6, 2, 5, 3], [6, 2, 5, 4], [6, 3, 1, 5], [6, 3, 2, 5], [6, 3, 5, 1], [6, 3, 5, 2], [6, 4, 1, 5], [6, 4, 2, 5], [6, 4, 5, 1], [6, 4, 5, 2], [6, 5, 1, 3], [6, 5, 1, 4], [6, 5, 2, 3], [6, 5, 2, 4], [6, 5, 3, 1], [6, 5, 3, 2], [6, 5, 4, 1], [6, 5, 4, 2]]
Hey I'm trying to generate sublists of a list. For example I've a list like this:
l = [1,2,3,4,5,6,7,8,9,10,11,12]
I want to split them in sublists with the length of 4. But to first element is the same like the last element from the previous list AND like I said it must have the length of 4. Like this:
l1 = [1,2,3,4]
l2 = [4,5,6,7]
l3 = [7,8,9,10]
l4 = [10, 11, 12] <-- should be ignored
Does someone has an idea?! I'm thinking about an generator but I'm not quite sure.
A simple but flexible generator implementation:
def overlapping_sublists(l, n, overlap=1, start=0):
while start <= len(l) - n:
yield l[start:start+n]
start += n - overlap
Example usage:
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> list(overlapping_sublists(l, 4))
[[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9, 10]]
>>> list(overlapping_sublists(l, 4, 2, 3))
[[4, 5, 6, 7], [6, 7, 8, 9], [8, 9, 10, 11]]
a = []
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
for i in range(0, len(l)-3, 3):
a.append(l[i:i+4])
will give a = [[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9, 10]]
or you can use as a list comprehension:
[l[i:i+4] for i in range(0, len(l)-3, 3)]
print([l[i:i+4] for i in range(0, len(l), 3)])
Output:
[[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9, 10], [10, 11, 12]]
Only sublists of length 4:
print([m for m in [l[i:i+4] for i in range(0, len(l), 3)] if len(m) == 4])
Output:
[[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9, 10]]
Using generators:
for n in (m for m in (l[i:i+4] for i in range(0, len(l), 3)) if len(m) == 4):
print(n)
Output:
[1, 2, 3, 4]
[4, 5, 6, 7]
[7, 8, 9, 10]
I'm struggling to understand how to combine elements from a list.
I have:
a = [[1, 2], [3, 4, 5], [6], [7, 8, 9, 10]]
I want to create triplets by taking elements from each list members
but always including the first element. This piece of code does half the
job:
r=[[]]
for x in a[0:3]:
t = []
for y in x:
for i in r:
t.append(i+[y])
r = t
r
[[1, 3, 6], [2, 3, 6], [1, 4, 6], [2, 4, 6], [1, 5, 6], [2, 5, 6]]
but I also want:
[[1,6,7], [1,6,8], [1,6,9] etc.]
Could someone please suggest a good method to do that?
Using itertools:
import itertools
a = [[1, 2], [3, 4, 5], [6], [7, 8, 9, 10]]
for xss in itertools.combinations(a, 3): # pick 3 lists.
# xss => ([1, 2], [3, 4, 5], [6])
# ([1, 2], [3, 4, 5], [7, 8, 9, 10])
# ...
indexed = [enumerate(x) for x in xss]
# indexed => [[(0, 1), (1, 2)], [(0, 3), (1, 4), (2, 5)], [(0, 6)]]
# ^^^^^^^^^^^^^^^^ list(enumerate([1, 2]))
for xs in itertools.product(*indexed):
# xs => ((0, 1), (0, 3), (0, 6))
# ((0, 1), (0, 3), (0, 7))
# ((0, 1), (0, 6), (0, 7))
# ...
if all(i > 0 for i, x in xs): # exclude no first item is selected.
continue
print [x for i, x in xs]
UPDATE response to the comment.
import itertools
a = [[1, 2], [3, 4, 5], [6], [7, 8, 9, 10]]
for xss in itertools.combinations(a[1:], 2):
xss = (a[0],) + xss
indexed = [enumerate(x) for x in xss]
for xs in itertools.product(*indexed):
if all(i > 0 for i, x in xs):
continue
print [x for i, x in xs]
output:
[1, 3, 6]
[1, 4, 6]
[1, 5, 6]
[2, 3, 6]
[2, 4, 6]
[2, 5, 6]
[1, 3, 7]
[1, 3, 8]
[1, 3, 9]
[1, 3, 10]
[1, 4, 7]
[1, 4, 8]
[1, 4, 9]
[1, 4, 10]
[1, 5, 7]
[1, 5, 8]
[1, 5, 9]
[1, 5, 10]
[2, 3, 7]
[2, 3, 8]
[2, 3, 9]
[2, 3, 10]
[2, 4, 7]
[2, 5, 7]
[1, 6, 7]
[1, 6, 8]
[1, 6, 9]
[1, 6, 10]
[2, 6, 7]
[2, 6, 8]
[2, 6, 9]
[2, 6, 10]