Related
If I run the code
import itertools
products = itertools.product([0,1],repeat=3)
print(list(products))
I get the following output:
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
However, I would only like to repeat the 0 once and the 1 twice. In other words, I want the following output:
[(0, 1, 1), (1, 0, 1), (1, 1, 0)]
How can I achieve this?
Of course, I could do:
import itertools
products = itertools.permutations([0,1,1],3)
print(list(set(products)))
but in my case there is a large number of elements so that calling set before iteration will kill the code due to memory issues.
If you only have 0's and 1's this would work:
from itertools import combinations
def gen(n0, n1):
n = n0 + n1
for c in combinations(range(n), n1):
out = [0]*n
for i in c:
out[i]=1
yield out
list(gen(1,2))
The way to build out may not be the most optimal, but the idea is there, I'll leave it to you to improve on it if time efficiency is an issue.
Generalizing one step further:
def gen(n0, n1, n2):
n12 = n1 + n2
n = n0 + n12
for c12 in combinations(range(n), n12):
out = [0]*n
for i in c12:
out[i] = 1
for c2 in combinations(c12, n2):
out_ = out.copy()
for i in c2:
out_[i] = 2
yield out_
Again the construction of out_ is likely suboptimal. And with the same idea you can keep nesting to more and more different elements. And if you have more possible elements that the depth becomes cumbersome to nest by hand, you can recursify the process, which is a fun exercise too:
def gen(ns, elems=None, C=None, out=None):
if elems is None:
elems = list(range(len(ns)))
else:
assert len(elems) == len(ns)
if out is None:
N = 1
for n in ns:
N *= n
out = [elems[0]]*N
C = range(N)
if len(ns) == 1:
yield out
else:
n = ns[-1]
e = elems[-1]
for c in combinations(C,n):
out_ = out.copy()
for i in c:
out_[i] = e
C_ = [i for i in C if i not in c]
yield from gen(ns[:-1], elems[:-1], C_, out_)
This might be overly specialized to your example, but
>>> from collections import namedtuple
>>> t = namedtuple('t', 'v1 v2 v3')
>>> [tuple(t(1,1,1)._replace(**{x: 0})) for x in t._fields]
[(0, 1, 1), (1, 0, 1), (1, 1, 0)]
Given a list of numbers, how many different ways can you add them together to get a sum S?
Example:
list = [1, 2]
S = 5
1) 1+1+1+1+1 = 5
2) 1+1+1+2 = 5
3) 1+2+2 = 5
4) 2+1+1+1 = 5
5) 2+2+1 = 5
6) 1+2+1+1 = 5
7) 1+1+2+1 = 5
8) 2+1+2 = 5
Answer = 8
This is what I've tried, but it only outputs 3 as the answer
lst = [1, 2]
i = 1
result = 0
while i <= 5:
s_list = [sum(comb) for comb in combinations_with_replacement(lst, i)]
for val in s_list:
if val == 5:
result += 1
i+= 1
print(result)
However, this outputs three. I believe it outputs three because it doesn't account for the different order you can add the numbers in. Any ideas on how to solve this.
The problem should work for much larger data: however, I give this simple example to give the general idea.
Using both itertools.combinations_with_replacement and permutations:
import itertools
l = [1,2]
s = 5
res = []
for i in range(1, s+1):
for tup in itertools.combinations_with_replacement(l, i):
if sum(tup) == s:
res.extend(list(itertools.permutations(tup, i)))
res = list(set(res))
print(res)
[(1, 2, 2),
(2, 2, 1),
(1, 1, 2, 1),
(1, 2, 1, 1),
(2, 1, 1, 1),
(1, 1, 1, 2),
(2, 1, 2),
(1, 1, 1, 1, 1)]
print(len(res))
# 8
How about using dynamic programming? I believe it's more easy to understand and can be implemented easily.
def cal(target, choices, record):
min_choice = min(choices)
if min_choice > target:
return False
for i in range(0, target+1):
if i == 0:
record.append(1)
elif i < min_choice:
record.append(0)
elif i == min_choice:
record.append(1)
else:
num_solution = 0
j = 0
while j < len(choices) and i-choices[j] >= 0:
num_solution += record[i-choices[j]]
j += 1
record.append(num_solution)
choices = [1, 2]
record = []
cal(5, choices, record)
print(record)
print(f"Answer:{record[-1]}")
The core idea here is using an extra record array to record how many ways can be found to get current num, e.g. record[2] = 2 means we can use to ways to get a sum of 2 (1+1 or 2).
And we have record[target] = sum(record[target-choices[i]]) where i iterates over choices. Try to think, the way of getting sum=5 must be related with the way of getting sum=4 and so on.
Use Dynamic Programming.
We suppose that your list consists of [1,2,5] so we have this recursive function :
f(n,[1,2,5]) = f(n-1,[1,2,5]) + f(n-2,[1,2,5]) + f(n-5,[1,2,5])
Because if the first number in sum is 1 then you have f(n-1,[1,2,5]) options for the rest and if it is 2 you have f(n-2,[1,2,5]) option for the rest and so on ...
so start from f(1) and work your way up with Dynamic programming. this solution in the worst case is O(n^2) and this happens when your list has O(n) items.
Solution would be something like this:
answers = []
lst = [1,2]
number = 5
def f(target):
val = 0
for i in lst: #O(lst.count())
current = target - i
if current > 0:
val += answers[current-1]
if lst.__contains__(target): #O(lst.count())
val += 1
answers.insert(target,val)
j = 1;
while j<=number: #O(n) for while loop
f(j)
j+=1
print(answers[number-1])
here is a working version.
You'd want to use recursion to traverse through each possibility for each stage of addition, and pass back the numbers used once you've reached a number that is equal to the expected.
def find_addend_combinations(sum_value, addend_choices, base=0, history=None):
if history is None: history = []
if base == sum_value:
return tuple(history)
elif base > sum_value:
return None
else:
results = []
for v in addend_choices:
r = find_addend_combinations(sum_value, addend_choices, base + v,
history + [v])
if isinstance(r, tuple):
results.append(r)
elif isinstance(r, list):
results.extend(r)
return results
You could write the last part a list comprehension but I think this way is clearer.
Combinations with the elements in a different order are considered to equivalent. For example, #3 and #5 from your list of summations are considered equivalent if you are only talking about combinations.
In contrast, permutations consider the two collections unique if they are comprised of the same elements in a different order.
To get the answer you are looking for you need to combine both concepts.
First, use your technique to find combinations that meet your criteria
Next, permute the collection of number from the combination
Finally, collect the generated permutations in a set to remove duplicates.
[ins] In [01]: def combination_generator(numbers, k, target):
...: assert k > 0, "Must be a positive number; 'k = {}".format(k)
...: assert len(numbers) > 0, "List of numbers must have at least one element"
...:
...: for candidate in (
...: {'numbers': combination, 'sum': sum(combination)}
...: for num_elements in range(1, k + 1)
...: for combination in itertools.combinations_with_replacement(numbers, num_elements)
...: ):
...: if candidate['sum'] != target:
...: continue
...: for permuted_candidate in itertools.permutations(candidate['numbers']):
...: yield permuted_candidate
...:
[ins] In [02]: {candidate for candidate in combination_generator([1, 2], 5, 5)}
Out[02]:
{(1, 1, 1, 1, 1),
(1, 1, 1, 2),
(1, 1, 2, 1),
(1, 2, 1, 1),
(1, 2, 2),
(2, 1, 1, 1),
(2, 1, 2),
(2, 2, 1)}
I was working on part of a program in which I'm trying to input a list of numbers and return all groups of 3 numbers which sum to 0, without double or triple counting each number. Here's where I'm up to:
def threeSumZero2(array):
sums = []
apnd=[sorted([x,y,z]) for x in array for y in array for z in array if x+y+z==0]
for sets in apnd:
if sets not in sums:
sums.append(sets)
return sums
Is there any code I can put in the third line to make sure I don't return [0,0,0] as an answer.
This is my test list:
[-1,0,1,2,-1,4]
Thank you
*Edit: I should have clarified for repeated input values: the result expected for this test list is:
[[-1,-1,2],[-1,0,1]]
You want combinations without replacement, this is something offered by itertools. Your sums can then be made a set to remove the duplicates with regard to ordering.
from itertools import combinations
def threeSumZero2(array):
sums = set()
for comb in combinations(array, 3):
if sum(comb) == 0:
sums.add(tuple(sorted(comb)))
return sums
print(threeSumZero2([-1,0,1,2,-1,4]))
Output
{(-1, -1, 2), (-1, 0, 1)}
This solution can also be written more concisely using a set-comprehension.
def threeSumZero2(nums):
return {tuple(sorted(comb)) for comb in combinations(nums, 3) if sum(comb) == 0}
More efficient algorithm
Although, the above algorithm requires traversing all combinations of three items, which makes it O(n3).
A general strategy used for this kind of n-sum problem is to traverse the n-1 combinations and hash their sums, allowing to efficiently test them against the numbers in the list.
The algorithm complexity drops by one order of magnitude, making it O(n2)
from itertools import combinations
def threeSumZero2(nums, r=3):
two_sums = {}
for (i_x, x), (i_y, y) in combinations(enumerate(nums), r - 1):
two_sums.setdefault(x + y, []).append((i_x, i_y))
sums = set()
for i, n in enumerate(nums):
if -n in two_sums:
sums |= {tuple(sorted([nums[idx[0]], nums[idx[1]], n]))
for idx in two_sums[-n] if i not in idx}
return sums
print(threeSumZero2([-1,0,1,2,-1,4]))
Output
{(-1, -1, 2), (-1, 0, 1)}
You could do this with itertools (see Oliver's answer), but you can also achieve the result with three nested for-loops:
def threeSumZero2(lst):
groups = []
for i in range(len(lst)-2):
for j in range(i + 1, len(lst)-1):
for k in range(j + 1, len(lst)):
if lst[i] + lst[j] + lst[k] == 0:
groups.append((lst[i], lst[j], lst[k]))
return groups
and your test:
>>> threeSumZero2([-1, 0, 1, 2, -1, 4])
[(-1, 0, 1), (-1, 2, -1), (0, 1, -1)]
Oh and list != array!
I'm trying to find a way, using built-in functions, to list every way to organize N balls in M slots. The balls can stack in the slots. For example:
N = 2, M = 3 -> {|0|1|1|, |1|0|1|, |1|1|0|, |2|0|0|, |0|2|0|, |0|0|2|}
itertools.permutations() is part of the puzzle, but how can you go through all possible stacks of balls that preserves N?
Let oo represent two balls.
The problem of placing 2 balls in 3 slots is equivalent to the problem of placing 2 balls around 2 sticks:
|o|o --> 0,1,1
o||o 1,0,1
o|o| 1,1,0
oo|| 2,0,0
|oo| 0,2,0
||oo 0,0,2
The sticks, | represent the edges of the slots. The number of balls in each slot is shown on the right.
Notice that in each row there are 4 locations, and 2 sticks. There is always one fewer stick than there are slots. So the problem is equivalent to finding all the ways to select 2 locations for the balls out of 4 possibilities. 4 choose 2.
In [98]: import itertools as IT
In [99]: list(IT.combinations(range(4), 2))
Out[99]: [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
These, then, are the possible locations for the balls.
All that's left to do is to compute into which slot these balls belong. Let's take (1, 3) as an example. The pictoral diagram for (1, 3) looks like this:
|o|o
It turns out that if you subtract (0, 1) elementwise from (1, 3), you get the slot for each ball:
(1, 3)
(0, 1)
------
(1, 2)
Thus, the first ball is in slot 1, the second in slot 2.
In general, if you subtract range(m-1) from the combination, you get the slot values. This makes some sense if you think of the amount you are subtracting as the number of balls that precede the current ball in the pictoral diagram. Since our diagrams consist of balls and slots, if you subtract the balls, what remains are slots.
import itertools as IT
def pigeonhole(n, m):
"""
Generate the ways n balls can placed in m slots
"""
for choice in IT.combinations(range(n+m-1), n):
slot = [c-i for i,c in enumerate(choice)]
result = [0]*m
for i in slot:
result[i] += 1
yield result
print(list(pigeonhole(2,3)))
yields
[[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]]
To find all assignments of N balls to M slots:
if N is 0 then
leave all M slots empty
otherwise, if M is 1, then
put all N balls to the only slot
otherwise
For each i in 0 .. N
put i balls in M-th slot, and
find all assignments of remaining N-i balls to remaining M-1 slots
Oh, here's a fun way to come at it:
>>> import random
>>> def permutate(N, M, lim = 1e6):
... for i in range(int(lim)):
... tup = ()
... for j in range(M):
... tup += (random.randrange(0,N+1),)
...
... if sum(tup) == N: yield tup
...
>>> permutes = []
>>> for p in permutate(2, 3):
... if not p in permutes:
... permutes.append(p)
...
>>> permutes
[(0, 1, 1), (0, 0, 2), (2, 0, 0), (0, 2, 0), (1, 0, 1), (1, 1, 0)]
>>>
First we create a generator that gives a valid tuple of M elements which sums to N. We do this in a Monte Carlo-esk approach by composing each element of of a random number from 0 to N+1. We do this M times, and append each guess to a tuple. We then check if the tuple is valid (sums to N). We then call this generator repeatedly until it is exhausted, and add non-repeat entries to a list.
Some Things to Note:
You'll need to tune lim to be appropriate for what you need. Generally it should be higher for larger M and N. You could modify this (with some effort) to estimate if lim is sized appropriate since you can figure how long permutes should be based off of N and M.
Monte Carlo methods are slow, which is why I categorized this as a "fun" approach
Consider using itertools.combinations_with_replacement.
You can easily do like this.
import itertools
def myfunc(n, m):
for j in itertools.combinations_with_replacement(xrange(m), n):
r = [0] * m
for i in j:
r[i] += 1
yield r
The problem is how to ensure this is correct.
I think #wnnmaw answer can be useful for that, because the logic is very simple.
#http://stackoverflow.com/a/22940260/2931409
import random
def permutate(N, M, lim = 1e6):
for i in range(int(lim)):
tup = []#<--Sorry changed here for test
for j in range(M):
tup += (random.randrange(0,N+1),)
if sum(tup) == N: yield tup
def perm(n, m):
permutes = []
for p in permutate(n, m):
if not p in permutes:
permutes.append(p)
return permutes
OK. Now check myfunc correctly works.
>>> N, M = 3, 5
>>> sorted(myfunc(N, M)) == sorted(perm(N, M))
True
Here's a nice way to do it.
NI = sum(in_pop)#number of input balls
# create the seed vector to be folded
u0 = [0] * NO
for i in range(NI):
u0[i] = 1
# create a storage basis matrix and fold the seed vector
basis = [u0]
ui = []
for i in arange(1,NI):
# copy the last vector
ui = basis[i - 1][:]
# find the number to 0 boundary
edge = ui.index(0)
# set the previous value to zero
tmp = ui[edge - 1]
ui[edge - 1] = 0
# increment the value behind that
ui[edge - 2] = ui[edge - 2] + tmp
print 'edge # ', edge
print 'ui', ui
basis.append(ui[:])
print basis
# run through the permutations in this basis
orderings = []
for ui in basis:
perms = set(itertools.permutations(ui))
orderings.extend(perms)
print orderings
The result is the following for input N=2, M=3:
edge # 2
ui [2, 0, 0]
[[1, 1, 0], [2, 0, 0]]
[(0, 1, 1), (1, 1, 0), (1, 0, 1), (0, 2, 0), (2, 0, 0), (0, 0, 2)]
[Finished in 0.5s]
I want to generate all permutations of an n by n list with max value n-1, for example, for n=3, all the possible lists are as follows
[0,0,0]
[0,0,1]
[0,0,2]
[0,1,0]
[0,1,1]
[0,1,2]
[0,2,0]
...
[2,2,2]
I realize this grows very large very quickly (there are n^n permutations). I currently have the following working code which uses recursion
def generatePermutations(allPerms, curPerm, curIndex, n):
#allPerms is a reference to the full set of permutations
#curIndex is the index which is currently being changed
if (curIndex == n - 1):
for i in range(n):
curPerm[curIndex] = i
allPerms.append(list(curPerm))
else:
for i in range(n):
curPerm[curIndex] = i
#recursively generate all permutations past our curIndex
generatePermutations(allPerms, curPerm, curIndex + 1, n)
allPermutations = []
currentPermutation = []
n = 4
for i in range(n):
currentPermutation.append(0)
generatePermutations(allPermutations, currentPermutation, 0, n)
In trying to find a non-recursive solution I've hit a wall, I'm thinking there would have to be n number of nested for loops, which I can't figure out how to do for arbitrary n. The only ideas I've had are doing some kind of fancy adding of functions containing the loops to a list to be run somehow, or even more absurdly, generating the code programmatically and passing it to an eval call. My gut tells me these are more complicated than necessary. Can anyone think of a solution? thanks!
Simple way, with a library call:
import itertools
def lists(n):
return itertools.product(xrange(n), repeat=n)
This returns an iterator, rather than a list. You can get a list if you want by calling list on the result.
If you want to do this without foisting the job onto itertools, you can count in base n, incrementing the last digit and carrying whenever you hit n:
def lists(n):
l = [0]*n
while True:
yield l[:]
for i in reversed(xrange(n)):
if l[i] != n-1:
break
l[i] = 0
else:
# All digits were n-1; we're done
return
l[i] += 1
You can use itertools.permutations() to handle the whole problem for you, in one go:
from itertools import permutations
allPermutations = list(permutations(range(4))
The documentation includes Python code that details alternative implementations in Python for that function; a version using itertools.product(), for example:
from itertools import product
def permutations(iterable, r=None):
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
for indices in product(range(n), repeat=r):
if len(set(indices)) == r:
yield tuple(pool[i] for i in indices)
However, your expected output is just a product of range(3) over 3:
>>> from itertools import product
>>> for p in product(range(3), repeat=3):
... print p
...
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 1, 0)
(0, 1, 1)
(0, 1, 2)
(0, 2, 0)
(0, 2, 1)
(0, 2, 2)
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 1, 0)
(1, 1, 1)
(1, 1, 2)
(1, 2, 0)
(1, 2, 1)
(1, 2, 2)
(2, 0, 0)
(2, 0, 1)
(2, 0, 2)
(2, 1, 0)
(2, 1, 1)
(2, 1, 2)
(2, 2, 0)
(2, 2, 1)
(2, 2, 2)
Permutations is a much shorter sequence:
>>> from itertools import permutations
>>> for p in permutations(range(3)):
... print p
...
(0, 1, 2)
(0, 2, 1)
(1, 0, 2)
(1, 2, 0)
(2, 0, 1)
(2, 1, 0)