I have a list ls of length n and want to get all lists of length m (where m > n) that contain ls in the same order, plus (m - n) zeroes, inserted at each possible combination of positions.
For example: ls = [1, 2, 3] and m = 4 should return
[[1, 2, 3, 0],
[1, 2, 0, 3],
[1, 0, 2, 3],
[0, 1, 2, 3]]
and ls = [1, 2, 3] and m = 5 should return
[[1, 2, 3, 0, 0],
[1, 2, 0, 3, 0],
[1, 2, 0, 0, 3],
[1, 0, 2, 3, 0],
[1, 0, 2, 0, 3],
[1, 0, 0, 2, 3],
[0, 1, 2, 3, 0],
[0, 1, 2, 0, 3],
[0, 1, 0, 2, 3],
[0, 0, 1, 2, 3]]
The solution should be fast and memory efficient - in particular, it should avoid generating duplicate solutions. Any help is appreciated!
A working (but inefficient) attempt:
ls = [1, 2, 3]
m = 4
from itertools import permutations
n = len(ls)
results = []
for t in set(permutations('1' * n + '0' * (m - n))):
idxs = [i for i, j in enumerate(t) if j == '1']
result = [0] * m
for idx, value in zip(idxs, ls):
result[idx] = value
results.append(result)
Use itertools.combinations to generate every combination of places to insert zeroes. Then use a list comprehension to select 0 or the next original element to build the new list.
# Pad list orig with zeroes, out to "m" total elements.
from itertools import combinations
orig = [1, 2, 3]
m = 5
n = len(orig)
padded = []
for pad_idx in combinations(range(m), m-n):
t = orig[:]
padded.append( [0 if i in pad_idx else t.pop(0)
for i in range(m)] )
print(padded)
Output (formatted for readability):
[[0, 0, 1, 2, 3],
[0, 1, 0, 2, 3],
[0, 1, 2, 0, 3],
[0, 1, 2, 3, 0],
[1, 0, 0, 2, 3],
[1, 0, 2, 0, 3],
[1, 0, 2, 3, 0],
[1, 2, 0, 0, 3],
[1, 2, 0, 3, 0],
[1, 2, 3, 0, 0]]
Related
I have two different list of list, with different size (List A with size in range 1000 and list B with size in range 10,000).
A=[[0, 0, 0],
[0, 0, 1],
[0, 0, 2],
[0, 0, 3],
[0, 0, 4],
[0, 0, 5],
[0, 1, 0],
[0, 1, 1],
[0, 1, 2],
[0, 1, 3],
[0, 1, 4],
[0, 1, 5],
[0, 1, 6],
[0, 1, 7],
[0, 1, 8],
[0, 1, 9],
[0, 2, 0],
[0, 2, 1],
[0, 2, 2]]
B=[[1, 1, 2],
[0, 0, 2],
[0, 0, 1],
[4, 2, 2],
[3, 1, 2],
[1, 0, 1],
[1, 1, 2],
[0, 1, 2],
[0, 0, 0],
[2, 2, 3],
[1, 2, 1],
[0, 2, 1],
[0, 2, 0],
[0, 2, 1],
[0, 1, 3],
[0, 0, 0],
[1, 2, 5],
[0, 4, 3],
[0, 1, 3]]
I need to compare list with list B and find out how many times each element of A occur in B. For example I need to find out how many times [0,0,0] (first element of A) occur in B.
Thank you for your help.
This should work:
A = [[0, 0, 0],
[0, 0, 1],
[0, 0, 2],
[0, 0, 3],
[0, 0, 4],
[0, 0, 5],
[0, 1, 0],
[0, 1, 1],
[0, 1, 2],
[0, 1, 3],
[0, 1, 4],
[0, 1, 5],
[0, 1, 6],
[0, 1, 7],
[0, 1, 8],
[0, 1, 9],
[0, 2, 0],
[0, 2, 1],
[0, 2, 2]]
B = [[1, 1, 2],
[0, 0, 2],
[0, 0, 1],
[4, 2, 2],
[3, 1, 2],
[1, 0, 1],
[1, 1, 2],
[0, 1, 2],
[0, 0, 0],
[2, 2, 3],
[1, 2, 1],
[0, 2, 1],
[0, 2, 0],
[0, 2, 1],
[0, 1, 3],
[0, 0, 0],
[1, 2, 5],
[0, 4, 3],
[0, 1, 3]]
nums = []
for i in A:
for j in B:
if i in B:
nums.append(str(i))
nums_freq = {}
nums = list(dict.fromkeys(nums))
for i in nums:
count = 0
for j in B:
if i == str(j):
if i in nums_freq.keys():
nums_freq[i] += 1
else:
nums_freq[i] = 1
Value for num_freq:
{'[0, 0, 0]': 2,
'[0, 0, 1]': 1,
'[0, 0, 2]': 1,
'[0, 1, 2]': 1,
'[0, 1, 3]': 2,
'[0, 2, 0]': 1,
'[0, 2, 1]': 2}
>>> import operator
>>> for e in A:
... print(e, 'appearing in :', operator.countOf(B, e))
...
[0, 0, 0] appearing in : 2
[0, 0, 1] appearing in : 1
[0, 0, 2] appearing in : 1
[0, 0, 3] appearing in : 0
[0, 0, 4] appearing in : 0
[0, 0, 5] appearing in : 0
[0, 1, 0] appearing in : 0
[0, 1, 1] appearing in : 0
[0, 1, 2] appearing in : 1
[0, 1, 3] appearing in : 2
[0, 1, 4] appearing in : 0
[0, 1, 5] appearing in : 0
[0, 1, 6] appearing in : 0
[0, 1, 7] appearing in : 0
[0, 1, 8] appearing in : 0
[0, 1, 9] appearing in : 0
[0, 2, 0] appearing in : 1
[0, 2, 1] appearing in : 2
[0, 2, 2] appearing in : 0
I can suggest 2 different ways to do it:
Using a counter:
from collections import Counter
cb = Counter(tuple(b) for b in B)
list((a, cb[tuple(a)]) for a in A))
Using nested comprehensions:
list((a, sum(all(ia == ib for ia, ib in zip(a, b)) for b in B)) for a in A)
Actually, you should consider having the inner collection as tuples instead of lists, because tuples support element-wise equality and are hashable.
if A and B were lists of tuples, it would be as simple as:
Counter(a for a in A for b in B if a == b)
I need to find all the possible combinations of a list with a defined length and numbers of defined size, but subject to a restriction that does not allow having the same repeated number in the list (except 0).
Currently I have a recursive code that generates all the combinations, but when trying to create the constraints I lose many options.
I have tried the options that appear commented to solve it, but it still does not work for me. Thank you in advance for your help.
size=3
l=[0 for x in range(size)]
def recursive(l, index, num, final):
if index == len(l) or num == final:
return
# if num!=0 and num in l:
# return
l[index]=num
print(l)
recursive(l, index+1, 0, final)
recursive(l, index, num+1, final)
recursive(l, 0, 0, 4)
the expected output for a list of size 3 and final number 3 would be something like this
[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 0, 3]
[0, 1, 0]
[0, 1, 2]
[0, 1, 3]
[0, 2, 0]
[0, 2, 1]
[0, 2, 3]
[0, 3, 0]
[0, 3, 1]
[0, 3, 2]
[1, 0, 0]
[1, 0, 2]
[1, 0, 3]
[1, 2, 0]
[1, 2, 3]
[1, 3, 0]
[1, 3, 2]
[2, 0, 0]
[2, 0, 1]
[2, 0, 3]
[2, 1, 0]
[2, 1, 3]
[2, 3, 0]
[2, 3, 1]
[3, 0, 0]
[3, 0, 1]
[3, 0, 2]
[3, 1, 0]
[3, 1, 2]
[3, 2, 0]
[3, 2, 1]
Start with the powerset of the non-zero values (a function for computing the powerset of a list can be found in the itertools documentation.)
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
>>> list(powerset([1,2,3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
(You could compute the power set including 0, but you would still need to zero-pad the shorter elements, as well as discard (0,1,2,3)).
Each element of the powerset should next be padded with zeros so that they are all the same length.
Finally, you want the list of permutations (using itertools.permutations) of each 0-padded permutation of the original input.
Use itertools.combinations_with_replacement(), and then filter out the elements with duplicates.
def has_duplicates(l):
'''Return True if list has duplicate elements other than 0'''
l = [x in l if x != 0]
return len(l) != len(set(l))
result = [comb for comb in itertools.combinations_with_replacement(l, 3) if not has_duplicates(comb)]
You can make this lazy by using itertools.filterfalse(); this way you won't fill up memory with all combinations of a large list.
result = itertools.filterfalse(has_duplicates, itertools.combinations_with_replacement(l, 3))
Here is a solution based on counters, I am using a base 4 counter:
def charcnt(s, a):
ctr = [1 if x == a else 0 for x in s ]
return sum(ctr)
def my_range(start,end,base,step=1):
def Convert(n,base):
string = "0123"
if n < base:
return string[n]
else:
return Convert(n//base,base) + string[n%base]
return (Convert(i,base) for i in range(start,end,step))
# base-10 58 will produce base-4 321
counter = [x.zfill(3) for x in my_range(0, 58, 4)]
result = []
for s in counter:
if charcnt(s, '1') > 1 or charcnt(s, '2') > 1 or charcnt(s, '3') > 1:
continue
else:
result.append(s)
result = [[int(n) for n in list(s)] for s in result]
#print(len(result))
for i in result:
print(i)
Will produce:
[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 0, 3]
[0, 1, 0]
[0, 1, 2]
[0, 1, 3]
[0, 2, 0]
[0, 2, 1]
[0, 2, 3]
[0, 3, 0]
[0, 3, 1]
[0, 3, 2]
[1, 0, 0]
[1, 0, 2]
[1, 0, 3]
[1, 2, 0]
[1, 2, 3]
[1, 3, 0]
[1, 3, 2]
[2, 0, 0]
[2, 0, 1]
[2, 0, 3]
[2, 1, 0]
[2, 1, 3]
[2, 3, 0]
[2, 3, 1]
[3, 0, 0]
[3, 0, 1]
[3, 0, 2]
[3, 1, 0]
[3, 1, 2]
[3, 2, 0]
[3, 2, 1]
Task
I have an array with sizes
sizes = [5, 3, 1, 2]
and based on the sizes I want to create the following array
mapping = [0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]
Solution
My first attempt
mapping = []
ctr = 0
for i in range(len(sizes)):
for j in range(sizes[i]):
mapping.append(i)
ctr += 1
Shorter version
mapping = [[i for _ in range(sizes[i])] for i in range(len(sizes))]
mapping = list(itertools.chain(*mapping))
Question
One line version?
Is it possible to do it in just one line with a neat code?
Using enumerate
Ex:
sizes = [5, 3, 1, 2]
result = [i for i, v in enumerate(sizes) for _ in range(v)]
print(result)
Output:
[0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]
Another approach would be to multiply the indices into sublists [[0, 0, 0, 0, 0], [1, 1, 1], [2], [3, 3]]
then flatten the result with itertoo.chain.from_iterable:
>>> from itertools import chain
>>> sizes = [5, 3, 1, 2]
>>> list(chain.from_iterable([i] * x for i, x in enumerate(sizes)))
[0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3]
Could someone explain how to fix the below? I've read some explanations, but couldn't get my head around it...
Many thanks in advance!
k = 2 # number of possible values for each element, in this case 0 or 1
length = 3 # length of list
result = [0] * length # initialise list
results = []
# generate permutations of list
def permutations(i, k, length):
j = 0
while j < k:
result[i] = j
if i == length - 1:
print("Result: ", result)
results.append(result)
print("Results: ", results)
else:
permutations(i + 1, k, length)
j += 1
permutations(0, k, length)
Below the output. The problem is that all previous elements in the list are overwritten...
Result: [0, 0, 0]
Results: [[0, 0, 0]]
Result: [0, 0, 1]
Results: [[0, 0, 1], [0, 0, 1]]
Result: [0, 1, 0]
Results: [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
Result: [0, 1, 1]
Results: [[0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1]]
Result: [1, 0, 0]
Results: [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]]
Result: [1, 0, 1]
Results: [[1, 0, 1], [1, 0, 1], [1, 0, 1], [1, 0, 1], [1, 0, 1], [1, 0, 1]]
Result: [1, 1, 0]
Results: [[1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0]]
Result: [1, 1, 1]
Results: [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
You are appending the same list everytime. Modifying the list via that reference will propagate changes to every where the list object lives; it is the same list.
You should append a shallow copy instead, so the reference result only modifies the current list:
...
results.append(result[:])
Otherwise, you could create a new list object at the start of the function so each recursive call gets its own list:
def permutations(i, k, length):
result = []
...
I believe changing results.append(result) to result.append(result[:]) should fix the problem. It is because of the mutability of lists
What you implement can be described as repeated permutations or cartesian product.
There are k ** length lists or tuples that can be generated this way.
As with any combination, permutation or product, itertools can help you :
from itertools import product
k = 2 # number of possible values for each element, in this case 0 or 1
length = 3 # length of list
print(list(product(range(k), repeat=length)))
#[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
Done!
I have a list which contains lists,
testList = [[1, 0, 1], [1, 1, 1], [0, 0, 0], ...]
What I want to do is that add the list in groups of n, so if n is 2, the first two lists will be added together and the second two lists will be added together, it will be like [1, 0, 1] + [1, 1, 1] = [2, 1, 2].
I tried the following code,
group_len = 3
sumResultList = [[sum(x) for x in zip(*testList)] for group in [testList[i:i+group_len] for i in range(0, len(testList), group_len)]]
But the above code does not work, how do I solve this?
l = [[1, 0, 1], [1, 1, 1],
[0, 0, 0], [1, 0, 1],
[1, 1, 1], [1, 0, 1],
[1, 1, 1], [1, 1, 1]]
n = 2
print [[sum(x) for x in zip(*l[a:a+n])] for a in range(0,len(l),n)]
Output:
[[2, 1, 2], [1, 0, 1], [2, 1, 2], [2, 2, 2]]
Try this:
testList = [[1, 0, 1], [1, 1, 1], [0, 0, 0], [2,3,4], [2,3,4], [2,3,4]]
group_len = 2
res = [list(map(sum, zip(*testList[k:k+group_len]))) for k in range(0, len(testList), group_len)]
print(res)
output:
[[2, 1, 2], [2, 3, 4], [4, 6, 8]]