Related
I need to find a solution for the below problem in Python3. I tried itertools.combinations but not clear on how to do it.
Prepare a 7-digit number that sums to 5. Each digit can be between 0-4 only. Also, there can be repetitions. Valid example numbers are -
[ [2,1,1,0,0,1,0], [3,0,1,0,0,1,0], [0,0,0,4,0,0,1], [1,0,0,3,0,1,0], [1,1,1,1,0,1,0], ...... ]
As you can see, numbers may appear more than once in this list.
How can I create a list of all combinations meeting the criteria above?
You can get all that sum to 5 with:
list(p for p in itertools.product(range(5),repeat = 7) if sum(p) == 5)
This yields 455 solutions.
This function will find every combination, with repeated combinations, that sum to N:
from itertools import product
from typing import List, Tuple
def perm_n_digit_total(n_digits, total, choices) -> List[Tuple]:
return list(filter(
lambda x: sum(x) == total,
product(choices, repeat=n_digits)
))
Example:
perm_n_digit_total(3, 1, range(4))
Out[43]: [(0, 0, 1), (0, 1, 0), (1, 0, 0)]
perm_n_digit_total(7, 5, range(4))[::50]
Out[49]:
[(0, 0, 0, 0, 0, 0, 5),
(0, 0, 0, 3, 1, 1, 0),
(0, 0, 2, 0, 3, 0, 0),
(0, 1, 0, 1, 3, 0, 0),
(0, 2, 0, 0, 1, 0, 2),
(0, 4, 1, 0, 0, 0, 0),
(1, 0, 1, 1, 1, 0, 1),
(1, 1, 1, 1, 1, 0, 0),
(2, 0, 1, 0, 0, 2, 0),
(3, 1, 0, 0, 0, 1, 0)]
Here's an itertools'less recursive solution.
def find_solutions(target, numbers, depth, potential_solution=[]):
if depth == 0:
if sum(potential_solution) == target:
print(potential_solution)
return
current_sum = sum(potential_solution)
for n in numbers:
new_sum = current_sum + n
if new_sum > target:
continue
find_solutions(target, numbers, depth - 1, potential_solution + [n])
find_solutions(target=5, numbers=[0,1,2,3,4], depth=7)
Output
[0, 0, 0, 0, 0, 1, 4]
[0, 0, 0, 0, 0, 2, 3]
[0, 0, 0, 0, 0, 3, 2]
[0, 0, 0, 0, 0, 4, 1]
[0, 0, 0, 0, 1, 0, 4]
[0, 0, 0, 0, 1, 1, 3]
...
[3, 1, 1, 0, 0, 0, 0]
[3, 2, 0, 0, 0, 0, 0]
[4, 0, 0, 0, 0, 0, 1]
[4, 0, 0, 0, 0, 1, 0]
[4, 0, 0, 0, 1, 0, 0]
[4, 0, 0, 1, 0, 0, 0]
[4, 0, 1, 0, 0, 0, 0]
[4, 1, 0, 0, 0, 0, 0]
If I got it, you need something like this:
import itertools
value = [0, 1, 2, 3, 4]
p = itertools.product(value, repeat=7)
for j in list(p):
print(j)
As each digit can only take 5 unique values - you would require itertools.combinations_with_replacement -
from itertools import combinations_with_replacement
zero_four = list(range(5))
for c in combinations_with_replacement(zero_four, 7):
if sum(c) == 5:
print(c)
This will give you all possible combinations that sum to 5 but not all the permutations -
Output
(0, 0, 0, 0, 0, 1, 4)
(0, 0, 0, 0, 0, 2, 3)
(0, 0, 0, 0, 1, 1, 3)
(0, 0, 0, 0, 1, 2, 2)
(0, 0, 0, 1, 1, 1, 2)
(0, 0, 1, 1, 1, 1, 1)
To get all permutations - you can use the itertools.permutations but since your output can have repeated elements, you will need to use a set to retain only unique permutations -
for c in combinations_with_replacement(zero_four, 7):
if sum(c) == 5:
print(set(permutations(c)))
Consider all arrays of l non-negative integers in the range 0,...,m. I would like to iterate (using a generator) over only those whose sum is exactly s.
For example, take l=7, s=5, m=4, the iteration could look like:
(0, 0, 0, 0, 0, 1, 4)
(0, 0, 0, 0, 0, 2, 3)
(0, 0, 0, 0, 0, 3, 2)
(0, 0, 0, 0, 0, 4, 1)
(0, 0, 0, 0, 1, 0, 4)
(0, 0, 0, 0, 1, 1, 3)
(0, 0, 0, 0, 1, 2, 2)
(0, 0, 0, 0, 1, 3, 1)
(0, 0, 0, 0, 1, 4, 0)
[...]
(3, 2, 0, 0, 0, 0, 0)
(4, 0, 0, 0, 0, 0, 1)
(4, 0, 0, 0, 0, 1, 0)
(4, 0, 0, 0, 1, 0, 0)
(4, 0, 0, 1, 0, 0, 0)
(4, 0, 1, 0, 0, 0, 0)
(4, 1, 0, 0, 0, 0, 0)
I don't mind which order the iteration happens in but I would like it to be efficient.
A really dumb way to reproduce the above that is far too slow for larger values of the variables is:
import itertools
s = 5
l = 7
m = 5
for arr in itertools.product(range(m), repeat=l):
if sum(arr) == s:
print(arr)
Think of the problem this way, you want to put s balls in l buckets with no more than m balls in any one bucket.
Since I know how to add one ball at a time, my instinct is to solve this using recursion. The base case is putting 0 balls instead of s and to go from one step to the next, adding 1 ball to each of the buckets that currently have less than m balls in them.
To make sure it's actually possible to complete the recursion, we first check there is enough places to put the balls.
# this helper function tells us the last non zero index in an array
def last_non_zero(arr):
indx = -1
while arr[indx] == 0 and indx > -len(arr):
indx -= 1
return len(arr) + indx
def balls_in_buckets(num_balls, num_buckets, max_balls):
assert num_buckets * max_balls >= num_balls, f"You can't put {num_balls} balls in {num_buckets} buckets without more than {max_balls} in a bucket."
if num_balls == 0:
yield ([0]*num_buckets).copy()
else:
for array in balls_in_buckets(num_balls - 1, num_buckets, max_balls):
for bucket_number in range(last_non_zero(array), num_buckets):
if array[bucket_number] < max_balls:
array_copy = array.copy()
array_copy[bucket_number] += 1
yield array_copy
Edit: Added code to remove duplicates
Edit: Performance improvement, takes about 2 seconds to generate the whole sequence for l=14, s=10, m=8. There are 1,143,870 items in the sequence.
What you are looking for are called "partitions". Unfortunately, there's some ambiguity as to whether "partitions" refers to splitting a set into partitions (e.g. [a,b,c] into [[a,b],[c]]), just the numbers characterizing size of each split (e.g. [2,1]), or the count of how many different splitting there are. The most promising search terms I found were "partition a number into k parts python", yielding Python Integer Partitioning with given k partitions and "python partition of indistinguishable items" yielding Partition N items into K bins in Python lazily . Those answers focus on partitions with at least one element, while you allow partitions to include zero elements. In addition, you seem to care about the order within a partition. For instance, you list (0, 0, 0, 0, 0, 1, 4), (0, 0, 0, 0, 0, 4, 1), and (0, 0, 0, 0, 1, 0, 4) as distinct partitions, while traditionally those would be considered equivalent.
I think the best way is to iterate through the buckets, and for each one iterate through the possible values. I changed the parameter names; l, s, and m and not very informative names, and "sum" and "max" are built-in functions. My current version, which may need more debugging:
def get_partitions(length, total, upper_bound):
if length == 1:
if total > upper_bound:
return []
return [[total]]
if total == 0:
return [[0]*length]
return [ [n]+sub_partition for
n in range(min(total, upper_bound)+1) for
sub_partition in get_partitions(
length-1, total-n, upper_bound)]
Side note: I initially read "iterate over the arrays" as meaning "go through the elements of the array". I think that the proper terminology is "iterate over the set of such arrays". When you say "iterate over x", x is being treated as the iterable, not the elements of the iterable.
By modyfing this answer, taking into account max_value:
def sums(length, total_sum, max_value):
if length == 1:
yield (total_sum,)
else:
for value in range(max(0, total_sum - (length - 1) * max_value),
min(max_value, total_sum) + 1):
for permutation in sums(length - 1, total_sum - value, max_value):
yield (value,) + permutation
L = list(sums(7,5, 4))
print('total permutations:',len(L))
# First and last 10 of list
for i in L[:10] + ['...'] + L[-10:]:
print(i)
total permutations: 455
(0, 0, 0, 0, 0, 1, 4)
(0, 0, 0, 0, 0, 2, 3)
(0, 0, 0, 0, 0, 3, 2)
(0, 0, 0, 0, 0, 4, 1)
(0, 0, 0, 0, 1, 0, 4)
(0, 0, 0, 0, 1, 1, 3)
(0, 0, 0, 0, 1, 2, 2)
(0, 0, 0, 0, 1, 3, 1)
(0, 0, 0, 0, 1, 4, 0)
(0, 0, 0, 0, 2, 0, 3)
...
(3, 1, 0, 0, 1, 0, 0)
(3, 1, 0, 1, 0, 0, 0)
(3, 1, 1, 0, 0, 0, 0)
(3, 2, 0, 0, 0, 0, 0)
(4, 0, 0, 0, 0, 0, 1)
(4, 0, 0, 0, 0, 1, 0)
(4, 0, 0, 0, 1, 0, 0)
(4, 0, 0, 1, 0, 0, 0)
(4, 0, 1, 0, 0, 0, 0)
(4, 1, 0, 0, 0, 0, 0)
data = (1,1,1,1,1)
dict_letters = {(1,1,1,1,1) : 'A',
(0,1,1,0,1) : 'B',
(1,1,1,1,1) : 'C',
(1,0,1,0,1) : 'D'}
def search():
for key in dict_letters:
if data == key:
print(dict_letters[key])
search()
#when running, this would result in only 'C' being printed; 'A' was never printed
Previously in my code, I obtained a unique 5 item tuple that was made up of 0's and 1's (the data tuple). The 5 items were essential for differentiating most letters such as 'B' and 'D' from the rest in my dictionary. However, I was faced with a problem when there were two keys that had the same 5 items, so I added 10 more items (other identifying data) to the tuple to help further differentiate the keys; this is an excerpt from my current dictionary:
data = (0,1,1,0,1,1,1,1,1,1,1,1,1,1,1)
data = (0,1,1,0,1,0,0,0,0,0,0,0,0,0,0)
data = (0,1,1,0,1,0,0,1,1,1,0,0,1,0,1)
#x = 0 or 1
dict_letters = {(1,1,1,1,1,x,x,1,x,x,x,x,x,x,x) : 'A',
(0,1,1,0,1,x,x,x,x,x,x,x,x,x,x) : 'B',
(1,1,1,1,1,x,x,0,x,x,x,x,x,x,x) : 'C',
(1,1,0,0,1,x,x,x,x,x,x,x,x,x,x) : 'D'}
def search():
for key in dict_letters:
if data == key:
print(dict_letters[key])
search()
#I need to find a way for all of the data tuples to print 'B' after running the program
In this excerpt, I have only created conditions for 1 of the additional items to differentiate 'A' from 'C'. I was wondering if it was possible to disregard if the other 9 additional items were 0 or 1 since they are not useful in the differentiation of these two keys (the items I wish to disregard are marked with x). I would also like to disregard the 10 additional items for 'B' and 'D' because the first 5 items are sufficient for identification. For example, I want a way for (0,1,1,0,1,0,0,0,0,0,0,0,0,0,0),(0,1,1,0,1,1,1,1,1,1,1,1,1,1,1),(0,1,1,0,1,0,0,1,1,1,0,0,1,0,1), and etc to be all read as 'B' without coding 2047 extra keys for 'B'.
I tried setting x = 0 or 1, and x = 0 and 1 before the dictionary but these does not work since I found that the key is then set to (0,1,1,0,1,1,1,1,1,1,1,1,1,1,1) for some reason after running the program.
Note: I am looking at using all the additional items at least once in the future, so removing any of the 10 additional items is not an option.
I am also relatively new to Python so I would appreciate it if you could make your answers as simple as possible. Thank you in advance!
We can use a plain dict for this task, we just have to build all of the possible keys for each letter. Your key tuples contain 15 items, with each item having 2 different values so there's a maximum of only 2**15 = 32768 different patterns, that's quite small on a modern machine.
We can use itertools.product to generate all the patterns efficiently. product effectively creates nested for loops from the args you pass it. Here's a short illustration of the technique. The following code generates all the patterns corresponding to 10XX01.
from itertools import product
for t in product(*[(1,), (0,), (0, 1), (0, 1), (0,), (1,)]):
print(t)
output
(1, 0, 0, 0, 0, 1)
(1, 0, 0, 1, 0, 1)
(1, 0, 1, 0, 0, 1)
(1, 0, 1, 1, 0, 1)
Here's some code that uses the data given in the question to build a dict you can use for your searches. We use the dict.get method so that if you look up a pattern that isn't in the dict the code returns None.
from __future__ import print_function
from itertools import product
#x = 0 or 1
X = 'x'
letter_patterns = {
(1, 1, 1, 1, 1, X, X, 1, X, X, X, X, X, X, X): 'A',
(0, 1, 1, 0, 1, X, X, X, X, X, X, X, X, X, X): 'B',
(1, 1, 1, 1, 1, X, X, 0, X, X, X, X, X, X, X): 'C',
(1, 1, 0, 0, 1, X, X, X, X, X, X, X, X, X, X): 'D',
}
def make_dict(letter_patterns):
''' Build a dict of all the bit patterns for each letter '''
xlate = {0: (0,), 1: (1,), X: (0, 1)}
letter_dict = {}
# Generate all of the (0, 1) combinations for each X in each pattern
for pattern, letter in letter_patterns.items():
for key in product(*[xlate[u] for u in pattern]):
letter_dict[key] = letter
return letter_dict
# test
letter_dict = make_dict(letter_patterns)
test_items = [
((1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0), 'A'),
((1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1), 'A'),
((1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), 'A'),
((0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), 'B'),
((0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'B'),
((0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1), 'B'),
((1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'C'),
((1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), 'C'),
((1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), 'C'),
((1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'D'),
((1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1), 'D'),
((1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), 'D'),
((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'Z'),
]
# Check that each test key gets the correct letter, or returns
# None if the key isn't in letter_dict
for key, true_letter in test_items:
letter = letter_dict.get(key)
print(key, true_letter, letter, letter == true_letter)
output
(1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0) A A True
(1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) A A True
(1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0) A A True
(0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) B B True
(0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) B B True
(0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1) B B True
(1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) C C True
(1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1) C C True
(1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) C C True
(1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) D D True
(1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1) D D True
(1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) D D True
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) Z None False
This code runs correctly on both Python 2 and 3 (tested on 2.6.6 and 3.6.0). You can make it slightly more efficient on Python 2 by changing
for pattern, letter in letter_patterns.items():
to
for pattern, letter in letter_patterns.iteritems():
The tuples in letter_patterns are not very convenient, especially if you want to put a lot of symbols into letter_patterns. To reduce the typing, rather than using those tuples we can use strings. Here's a variation of the above code that does that. The resulting letter_dict still uses tuple keys, since I assume that's what you get from your Leap Motion Device hardware.
letter_patterns = {
'A': '11111xx1xxxxxxx',
'B': '01101xxxxxxxxxx',
'C': '11111xx0xxxxxxx',
'D': '11001xxxxxxxxxx',
}
def make_dict(letter_patterns):
''' Build a dict of all the bit patterns for each letter '''
xlate = {'0': (0,), '1': (1,), 'x': (0, 1)}
letter_dict = {}
# Generate all of the (0, 1) combinations for each X in each pattern
for letter, pattern in letter_patterns.items():
for key in product(*[xlate[u] for u in pattern]):
letter_dict[key] = letter
return letter_dict
My understanding is that given some tuple as key, you want some entries in this tuple to be ignored if they do not match an existing key exactly.
You can do that by implementing your own dictionary-like class with the help of collections.UserDict and a custom __getitem__ method.
The following implementation assumes that entries in the tuple are either 1 or 0. Without that assumption, it would have to traverse all keys.
from UserDict import UserDict
# for Python 3 use this import instead:
# from collections import UserDict
from itertools import product
class WildcardDict(UserDict):
def __getitem__(self, args):
item, *wildcards = args
try:
return self.data[item]
except KeyError:
for xs in product((0, 1), repeat=len(wildcards)):
xs = iter(xs)
item = tuple(next(xs) if i in wildcards else x for i, x in enumerate(item))
if item in self.data:
return self.data[item]
raise KeyError(args)
d = WildcardDict()
d[0, 1, 1, 0, 1] = 'B'
print(d[(0, 1, 1, 0, 1), ]) # 'B'
print(d[(0, 1, 0, 0, 0), 2, 4]) # 'B'
Note that dict item lookup is usually O(1), although this makes it O(2k) where k is the number of wildcards. In particular, this means that if the number of wildcards was ever to grow, you would be better using a list where lookup would be O(n).
As i am beginner i need to find out the tuple which has only one value in it. for ex
a = [4, 0, 0, 4, 0, 0]
b = [0, 0, 0, 0, 0, 0]
d = [5, 0, 5, 0, 0, 0]
f = [0, 1, 0, 0, 0, 0]
This is lists value, by zipping it i get [(4, 0, 5, 0), (0, 0, 0, 1), (0, 0, 5, 0), (4, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)] this value.
In this i want to select which as only one value in tuples for ex my output should look like this [(0, 0, 0, 1), (4, 0, 0, 0)].
Please help me on it
Using a list comprehension:
[x for x in zipped if len(x) == x.count(0) + 1]
I think the intentions of this code are clear. I want to have in X all possible input words, with each digit being an element in a list. The following code works for 4 digits, but it gets unsustainable for bigger words. How can I make it more scalable? Let's assume I want the words of n digits instead of four.
d = [0,1]
X = [[x1,x2,x3,x4] for x1 in d for x2 in d for x3 in d for x4 in d]
You can use itertools.product for that:
from itertools import product
d = [0,1]
x = [list(t) for t in product(d,repeat=4)]
This gives:
>>> x
[[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 0, 1, 1], [0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 1, 0], [0, 1, 1, 1], [1, 0, 0, 0], [1, 0, 0, 1], [1, 0, 1, 0], [1, 0, 1, 1], [1, 1, 0, 0], [1, 1, 0, 1], [1, 1, 1, 0], [1, 1, 1, 1]]
And by modifying repeat= to for instance 5 you get all possible 5-digit lists.
In case you do not need lists - the elements are not supposed to be altered - tuples can be used, and in that case you can drop the list(..) construction:
# list of tuples
from itertools import product
d = [0,1]
x = list(product(d,repeat=4))
This generates:
>>> x
[(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0), (0, 0, 1, 1), (0, 1, 0, 0), (0, 1, 0, 1), (0, 1, 1, 0), (0, 1, 1, 1), (1, 0, 0, 0), (1, 0, 0, 1), (1, 0, 1, 0), (1, 0, 1, 1), (1, 1, 0, 0), (1, 1, 0, 1), (1, 1, 1, 0), (1, 1, 1, 1)]
Note that product(..) itself is a generator: it generates the elements lazily. This can be useful if the number of resulting elements will be huge and you can process them one-at-a-time. In that case you thus better do not construct a list. You can for instance use:
for tup in product(d,repeat=4):
print(tup)
To print all tuples. Although the effect would be the same as with a for tup in x:, now the memory usage can be lower (it depends on the implementation of the garbage collector in the interpreter): since not all tuples have to be in memory at the same time. The next tuple the generator emit can possibly reuse the memory the previous tuple took since that place is now vacant (at least given you do not store the tuples in a list, etc.)