I have an integer S and a collection A of integers. I need to find a set of integers from the collection where the sum of those integers is equal to S. It could be 1 integer or 50 - it doesn't matter.
I'm trying to do this like this:
I have an array res and an array grp
res starts with [0], grp is the initially given collection, and S is the sum we're trying to find, S is global
my function takes (res, grp)
I want to do this : (and example)
-
and stop when the sum of the res elements is equal to S
but I suck with recursion and I have no idea what I should be doing
this is my code
S = 7
grp = [0,5,6,4,3]
def sumFinder(res,grp):
if grp == []:
return grp #in case no pair was found [] is returned
if sum(res) == S:
return res
for i in range (0,len(grp)):
print(res)
print(grp[i])
res += [grp[i]]
newgrp = grp[:i]
newgrp += grp[i+1:]
return sumFinder(res,newgrp)
print(sumFinder([0],grp))
UPDATE:
thank you everyone for you answers
thank you englealuze for giving me a beter idea about approaching the problem thanks o you i got to this:
1 - this is for finding the first combination and returning it (this was my goal)
grp = [1,0,1,0,1,2,6,2,3,5,6,7,8,9,2,1,1,9,6,7,4,1,2,3,2,2]
S = 55
grps = []
def findCombination(comb,grp):
for i in range (0,len(grp)):
comb += [grp[i]]
newgrp = grp[:i]
newgrp += grp[i+1:]
if sum(comb) == S:
return comb
if newgrp not in grps:
grps.append(newgrp)
res = findCombination([],newgrp)
if res != None:
return res
print(findCombination([],grp))
2- this is for finding all the combinations: (this is the problem englealuze talked about, but i didn't understand his method that well even though it seems better)
grp = [1,0,1,1,9,6,7,4,1,2,3,2,2]
S = 12
grps = []
combinations = []
def findAllCombinations(comb,grp):
global combinations
for i in range (0,len(grp)):
comb += [grp[i]]
newgrp = grp
newgrp = grp[:i]
newgrp += grp[i+1:]
if sum(comb) == S and tuple(comb) not in combinations:
combinations.append(tuple(comb))
if newgrp not in grps:
grps.append(newgrp)
findAllCombinations([],newgrp)
findAllCombinations([],grp)
print(combinations)
my only problem now is that when S > 50 (in the first one), it takes longer to find the answer
what advice could you guys give me to improve both algorithms?
In stead of just providing code, I will show you how to think about this problem and how to tackle this type of problems in a general sense.
First, let us rephrase your question. In fact what you want is for a given set of numbers, find the combinations within the set which fulfill certain condition. So, you can decompose your question into 2 distinct steps.
Find all combinations of your set
Filter out the combinations that fulfill certain conditions
Let us think about how to solve the first task recursively. Remember, if a problem can be solved in a recursive way, it generally means there are some recursive patterns within your data and it usually can be solved in a very simple and clear way. If you end up with a messy recursive solution, it pretty much means you are in a wrong direction.
Let us see the pattern of your data first. If you have a very small set (1, 2), then the combinations out of this set are
1
2
1, 2
Let us increase one member to the set, (1, 2, 3). For this bigger set, all combiantions are
1 | 1, 3
2 | 2, 3
1, 2 | 1, 2, 3
| 3
Let us look at even bigger set (1, 2, 3, 4). The possible combinations are
1 1, 3 | 1, 3, 4
2 2, 3 | 2, 3, 4
1, 2 1, 2, 3 | 1, 2, 3, 4
3 | 3, 4
| 4
Now you see something interesting, the combinations of one bigger set is the combinations of the smaller set plus the additional element appending to every previous combination and plus the additional element itself.
Assume you already got the solution of all combinations of set with certain size, the solution of a bigger set can be derived from this solution. This naturally forms a recursion. You can translate this plain English directly to a recursive code as below
# assume you have got all combinations of a smaller set -> combinations(smaller(L))
# the solution of your bigger set can be directly derived from it with known new element
def combinations(L):
if L == []:
return []
else:
return next_solution(combinations(smaller(L)), newitem(L))
Notice how we decompose out task of solving a larger problem to solving smaller problems. You need the helper functions as below
# in your case a smaller set is just the new set drop one element
def smaller(L):
return L[1:]
# the new element would be the first element of new set
define newitem(L):
return L[0]
# to derive the new solution from previous combinations, you need three parts
# 1. all the previous combinations -> L
# 2. new item appending to each previous combination -> [i + [newelement] for i in L]
# 3. the new item itself [[newelement]]
def next_solution(L, newelement):
return L + [i + [newelement] for i in L] + [[newelement]]
Now we know how to get all combinations out of a set.
Then to filter them, you cannot just insert the filter directly into your recursive steps, since we rely on our previous solution to build up the result list recursively. The simple way is to filter the list while we obtain the full result of all combinations.
You will end up with a solution as this.
def smaller(L):
return L[1:]
def newitem(L):
return L[0]
def next_solution(L, newelement):
return L + [i + [newelement] for i in L] + [[newelement]]
def filtersum(L, N, f=sum):
return list(filter(lambda x: f(x)==N, L))
def combinations(L):
if L == []:
return []
else:
return next_solution(combinations(smaller(L)), newitem(L))
def filter_combinations(L, N, f=filtersum):
return f(combinations(L), N)
print(filter_combinations([0,5,6,4,3], 7))
# -> [[3, 4], [3, 4, 0]]
You can save some computations by filter out in each recursive call the combinations with sum bigger than your defined value, such as
def combinations(L):
if L == []:
return []
else:
return next_solution(list(filter(lambda x: sum(x) <=5, combinations(smaller(L)))), newitem(L))
print(combinations([1,2,3,4]))
# -> [[4], [3], [3, 2], [2], [4, 1], [3, 1], [3, 2, 1], [2, 1], [1]]
In fact there will be different ways to do recursion, depends on the way how you decompose your problem to smaller problems. There existed some smarter ways, but the approach I showed above is a typical and general approach for solving this type of problems.
I have example of solving other problems with this way
Python: combinations of map tuples
Below codes work (remove 'directly return' in the loop, changed to conditional 'return').
But I don't think it is a good solution. You need to improve your algorithm.
PS: This codes also will only return one match instead of all.
S = 7
grp = [0,3,6,4,6]
result = []
def sumFinder(res,grp, result):
for i in range (0,len(grp)):
temp = list(res) #clone res instead of direct-reference
if grp == [] or not temp:
return grp #in case no pair was found [] is returned
if sum(temp) == S:
result.append(tuple(temp))
return temp
temp.append(grp[i])
newgrp = grp[:i] + grp[i+1:]
sumFinder(list(temp),newgrp, result)
sumFinder([0], grp, result)
print result
Test Cases:
S = 7
grp = [0,3,6,4,6]
result = [(0, 0, 3, 4), (0, 0, 4, 3), (0, 3, 0, 4), (0, 3, 4), (0, 4, 0, 3), (0, 4, 3)]
[Finished in 0.823s]
Can you let me know where did you find this problem? I love to solve this type of problem, Bdw here is my approach:
a=[[[0],[0,5,6,4,3]]]
s=7
def recursive_approach(array_a):
print(array_a)
sub=[]
for mm in array_a:
array_1 = mm[0]
array_2 = mm[1]
if sum(array_2)==s:
return "result is",array_2
else:
track = []
for i in range(len(array_2)):
c = array_2[:]
del c[i]
track.append([array_1 + [array_2[i]], c])
sub.append(track)
print(sub)
return recursive_approach(sub[0])
print(recursive_approach(a))
output:
[[[0], [0, 5, 6, 4, 3]]]
[[[[0, 0], [5, 6, 4, 3]], [[0, 5], [0, 6, 4, 3]], [[0, 6], [0, 5, 4, 3]], [[0, 4], [0, 5, 6, 3]], [[0, 3], [0, 5, 6, 4]]]]
[[[0, 0], [5, 6, 4, 3]], [[0, 5], [0, 6, 4, 3]], [[0, 6], [0, 5, 4, 3]], [[0, 4], [0, 5, 6, 3]], [[0, 3], [0, 5, 6, 4]]]
[[[[0, 0, 5], [6, 4, 3]], [[0, 0, 6], [5, 4, 3]], [[0, 0, 4], [5, 6, 3]], [[0, 0, 3], [5, 6, 4]]], [[[0, 5, 0], [6, 4, 3]], [[0, 5, 6], [0, 4, 3]], [[0, 5, 4], [0, 6, 3]], [[0, 5, 3], [0, 6, 4]]], [[[0, 6, 0], [5, 4, 3]], [[0, 6, 5], [0, 4, 3]], [[0, 6, 4], [0, 5, 3]], [[0, 6, 3], [0, 5, 4]]], [[[0, 4, 0], [5, 6, 3]], [[0, 4, 5], [0, 6, 3]], [[0, 4, 6], [0, 5, 3]], [[0, 4, 3], [0, 5, 6]]], [[[0, 3, 0], [5, 6, 4]], [[0, 3, 5], [0, 6, 4]], [[0, 3, 6], [0, 5, 4]], [[0, 3, 4], [0, 5, 6]]]]
[[[0, 0, 5], [6, 4, 3]], [[0, 0, 6], [5, 4, 3]], [[0, 0, 4], [5, 6, 3]], [[0, 0, 3], [5, 6, 4]]]
[[[[0, 0, 5, 6], [4, 3]], [[0, 0, 5, 4], [6, 3]], [[0, 0, 5, 3], [6, 4]]], [[[0, 0, 6, 5], [4, 3]], [[0, 0, 6, 4], [5, 3]], [[0, 0, 6, 3], [5, 4]]], [[[0, 0, 4, 5], [6, 3]], [[0, 0, 4, 6], [5, 3]], [[0, 0, 4, 3], [5, 6]]], [[[0, 0, 3, 5], [6, 4]], [[0, 0, 3, 6], [5, 4]], [[0, 0, 3, 4], [5, 6]]]]
[[[0, 0, 5, 6], [4, 3]], [[0, 0, 5, 4], [6, 3]], [[0, 0, 5, 3], [6, 4]]]
('result is', [4, 3])
Related
Given a list of N elements, e.g.
mylist = [0, 1, 2, 3, 4, 5, 6, 7]
I want to find all the combinations of K elements in L groups, e.g. for K=4, L=2,
it will results as
L=0 L=1
1) [0, 1, 2, 3] [4, 5, 6, 7]
2) [0, 1, 2, 4] [3, 5, 6, 7]
3) [0, 1, 2, 5] [3, 4, 6, 7]
... etc...
69) [4, 5, 6, 7] [0, 1, 2, 3]
Notice that [0, 1, 2, 3] and [0, 1, 3, 2] would be counted as the same combination for the first group.
For the case L=2, I am using the following
from itertools import combinations
N = 8
M = 4
L = N // M
combs = list(combinations(range(N), M))
allidx = list(range(N))
for c, comb in enumerate(combs):
idx1 = list(comb)
idx2 = list(set(allidx) - set(idx1))
print(c, idx1,'\t',idx2)
First, what is the mathematical definition of this type of 'combinations'?
Second, In the case L>2, is there a more efficient way to compute them than computing all the permutations and prune them after?
You could use a recursive function, getting all combinations of k elements from the list, and combining them with the combinations for the remaining elements.
import itertools
def combs(lst, k, l):
if l == 0:
yield []
else:
for c in itertools.combinations(lst, k):
rest = [x for x in lst if x not in c]
for res in combs(rest, k, l-1):
yield [c, *res]
mylist = [0, 1, 2, 3, 4, 5, 6, 7]
for res in combs(mylist, 4, 2):
print(res)
Here, the part rest = [x for x in lst if x not in c] will only work if elements in the list are unique. If there can be duplicate elements, you can instead just get combinations of indices instead, e.g. like this (rest remains the same):
for idc in itertools.combinations(range(len(lst)), k):
comb = [lst[i] for i in idc]
idc_set = set(idc)
rest = [x for i, x in enumerate(lst) if i not in idc_set]
for res in combs(rest, k, l-1):
yield [comb, *res]
(Also, this assumes that lst has at least l*k elements.)
Here is approach that generates all unique partitions of size k from the set of length n.
Number of such partitions is (p is number of parts, equal to your L):
NPK(n,k) = n! / ((k!)^p * p!)
and grows fast (280 for n=9,k=3).
Algorithm recursively distributes items over parts. To avoid repeated generation of the same partition (like 01 23 45 and 01 45 23), we should restrict places for leading (the smallest) element of every group.
Here I used lastfilled parameter for index of the rightmost filled part, so item 0 always belongs to the 0-th part, item 1 might fall into parts 0 or 1 but not into part 2 and so on. Having intermediate result 01 __ __ we can make only 01 2_ __ at the next level, not 01 __ 2_.
def genp(l, parts, cnts, n, k, p, m, lastfilled):
if m == n:
l.append(parts[:])
return
for i in range(min(p, lastfilled + 2)):
if cnts[i] < k:
parts[i][cnts[i]] = m
cnts[i] += 1
genp(l, parts, cnts, n, k, p, m+1, max(i, lastfilled))
cnts[i] -= 1
def genpartssizek(n, k):
l = []
p = n // k
parts = [[0]*k for _ in range(p)]
cnts = [0]*p
genp(l, parts, cnts, n, k, p, 0, -1)
return l
print(genpartssizek(6,2))
[[[0, 1], [2, 3], [4, 5]],
[[0, 1], [2, 4], [3, 5]],
[[0, 1], [2, 5], [3, 4]],
[[0, 2], [1, 3], [4, 5]],
[[0, 2], [1, 4], [3, 5]],
[[0, 2], [1, 5], [3, 4]],
[[0, 3], [1, 2], [4, 5]],
[[0, 4], [1, 2], [3, 5]],
[[0, 5], [1, 2], [3, 4]],
[[0, 3], [1, 4], [2, 5]],
[[0, 3], [1, 5], [2, 4]],
[[0, 4], [1, 3], [2, 5]],
[[0, 5], [1, 3], [2, 4]],
[[0, 4], [1, 5], [2, 3]],
[[0, 5], [1, 4], [2, 3]]]
I'm working with lists and I came up with a question. So basically, I have a 3D list in which I'm trying that after a for loop I maintain the second and penultimate element of the inner list, my code does the work, but it changes from a 3D list to a 2D list and I don't want that (this is just an example, mylist can have more elements) here is my code
mylist = [[[0, 4, 3, 5, 1], [0, 2, 1], [0, 2, 3, 1], [0, 3, 5, 1], [0, 2, 5, 3, 1]],
[[0, 4, 1], [0, 2, 1], [0, 4, 3, 1], [0, 4, 5, 1], [0, 4, 3, 1]]]
aux = []
for i in mylist:
for j in i :
if len(j) >= 4:
aux.append(j[1:-1])
print(aux)
# aux= [[4, 3, 5], [2, 3], [3, 5], [2, 5, 3], [4, 3], [4, 5], [4, 3]]
I got almost what I need, but I was expecting this output
aux = [[[4, 3, 5], [2, 3], [3, 5], [2, 5, 3]], [[4, 3], [4, 5], [4, 3]]] #Mantains the 3D dimension
I'm working on my list comprehension, but I'm not quite sure how to do this, this is what I try
aux = [[j[1:-1] if len(j) >= 4 for j in i] for i in mylist]
#But I get a syntax error
I don't know if there is a easier way to do this (must be, but I'm the guy who complicate things) maybe a a much easier list comprehension that I'm not seeing, so any help will be appreciated, thank you!
You got it right, only you have to move the if statement to after for in list comprehension:
aux = [[j[1:-1] for j in i if len(j)>=4] for i in mylist]
output:
[[[4, 3, 5], [2, 3], [3, 5], [2, 5, 3]], [[4, 3], [4, 5], [4, 3]]]
You were really close!
This should work. Look it over and see the logic involved
aux = [[j[1:-1] for j in i if len(j) >= 4] for i in mylist]
I have a nested list:
x=[[[0, 1, 2], [0, 1, 2]], [[0, 1], [0, 1, 2]], [[0]], [[0, 1], [0, 1, 2, 3, 4]]]
Now, the goal is to get a nested list with the same structure but with elements replaced by their "global" counting numbers. So, the desired output should look like this:
y=[[[0, 1, 2], [3, 4, 5]], [[6, 7], [8, 9, 10]], [[11]], [[12, 13], [14, 15, 16, 17, 18]]]
I fight with it for the last couple of hours but without success.
Ideally, I'd like to have a universal solution being able to work with an arbitrary depth of nesting.
Any help would be very much appreciated. Thank you in advance!
Here's a recursive solution that does the replacement in-place and relies on the type of the element being replaced. The idea is to keep track of the "global counter" and pass it into the recursive calls so that it knows what to replace elements with:
x = [[[0, 1, 2], [0, 1, 2]], [[0, 1], [0, 1, 2]], [[0]], [[0, 1], [0, 1, 2, 3, 4]]]
def replace(lst, i):
for j in range(len(lst)):
if isinstance(lst[j], list):
lst[j], i = replace(lst[j], i)
else:
lst[j] = i
i += 1
return lst, i - 1
replace(x, 0)
print(x)
# [[[0, 1, 2], [3, 4, 5]], [[6, 7], [8, 9, 10]], [[11]], [[12, 13], [14, 15, 16, 17, 18]]]
Here's another recursive solution. It uses itertools.count and builds a new list. Personally, I like to avoid integer indexing when possible for readability.
from itertools import count
def structured_enumerate(lst, counter=None):
'enumerate elements in nested list, preserve structure'
result = []
if counter is None:
counter = count()
for x in lst:
if isinstance(x, list):
result.append(structured_enumerate(x, counter))
else:
result.append(next(counter))
return result
Demo:
>>> x = [[[0, 1, 2], [0, 1, 2]], [[0, 1], [0, 1, 2]], [[0]], [[0, 1], [0, 1, 2, 3, 4]]]
>>> structured_enumerate(x)
[[[0, 1, 2], [3, 4, 5]],
[[6, 7], [8, 9, 10]],
[[11]],
[[12, 13], [14, 15, 16, 17, 18]]]
~edit~
Here's an attempt at a generic solution that works with any iterable, indexable or not, where you can specifiy iterable types to exclude from iteration.
from itertools import count
def structured_enumerate(iterable, dontiter=(str,), counter=None):
'enumerate elements in nested iterable, preserve structure'
result = []
if counter is None:
counter = count()
for x in iterable:
# check if x should be iterated
try:
iter(x)
is_iterable = True
except TypeError:
is_iterable = False
# strings of length zero and one are a special case
if isinstance(x, str) and len(x) < 2:
is_iterable = False
if is_iterable and not isinstance(x, dontiter):
subresult = structured_enumerate(x, dontiter, counter)
result.append(subresult)
else:
result.append(next(counter))
return result
Demo:
>>> fuzzy = [{0, 0}, '000', [0, [0, 0]], (0,0), 0]
>>> structured_enumerate(fuzzy)
[[0, 1], 2, [3, [4, 5]], [6, 7], 8]
>>> structured_enumerate(fuzzy, dontiter=())
[[0, 1], [2, 3, 4], [5, [6, 7]], [8, 9], 10]
>>> structured_enumerate(fuzzy, dontiter=(tuple, set))
[0, [1, 2, 3], [4, [5, 6]], 7, 8]
My task is to calculate the k-permutations from the updated List by new element
without recalculating the k-permutations already gotten from the previous state of the list. Example:
liste = [1, 2, 3]
3-permutations are:
[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]
The updated list:
liste = [1, 2, 3, 4]
I would like to obtain directly 3-permutations[1, 2, 3, 4]-3-permutations[1, 2, 3]
without recalculating 3-permutations[1, 2, 3]
Calculate directly the new permutations:
[1, 2, 4], [1, 3, 4], [1, 4, 2], [1, 4, 3], [2, 1, 4], [2, 3, 4], [2, 4, 1],
[2, 4, 3], [3, 1, 4], [3, 2, 4], [3, 4, 1], [3, 4, 2], [4, 1, 2], [4, 1, 3],
[4, 2, 1], [4, 2, 3], [4, 3, 1], [4, 3, 2]
Thanks
Computing first the cartesian product {0,1,2,3}x{0,1,2}x{0,1} and taking the nth element of list (1,2,3,4).
r=[]
for prd in itertools.product([[0,1,2,3],[0,1,2],[0,1]]):
l=[1,2,3,4]
r0=[]
for i in prd:
r0 += l[i]
del l[i]
r += r0
EDIT: original answer gives the 3-permutations of [1,2,3,4]
following command answers specifically to question, see how it can be generalized
[list(j) for i in itertools.combinations([1,2,3],2) for j in itertools.permutations(list(i)+[4])]
next case, maybe one of ?
[list(j) for i in itertools.combinations([1,2,3],2) for j in itertools.permutations(list(i)+[4,5])]
[list(j) for i in itertools.combinations([1,2,3,4],3) for j in itertools.permutations(list(i)+[4,5])]
try saving the existing permutations to a list, then do:
if newPermutation not in listOfExistingPermutations:
listOfExistingPermutations.append(newPermutation)
or something along those lines
Having a list of lists,
mylist = [[1, 3, 4], [3, 6, 7], [8, 0, -1, 3]]
I need a method to get list containing indexes of all elements of some certain value, '3' for example. So for value of '3' such list of indexes should be
[[0, 1], [1, 0], [2, 3]]
Thanks in advance.
Update: we can have several instances of sought-for value ('3' in our case) in same sublist, like
my_list = [[1, 3, 4], [3, 6, 7], [8, 0, -1, 3, 3]]
So desired output will be [[0, 1], [1, 0], [2, 3], [2, 4]].
I suppose that my solution is slightly naive, here it is:
my_list = [[1, 3, 4], [3, 6, 7], [8, 0, -1, 3, 3]]
value = 3
list_of_indexes = []
for i in range(len(my_list)):
for j in range(len(my_list[i])):
if my_list[i][j] == value:
index = i, j
list_of_indexes.append(index)
print list_of_indexes
>>[(0, 1), (1, 0), (2, 3), (2, 4)]]
It will be great to see more compact solution
Assuming the value appears once in each sublist, you could use the following function, which makes use of the built in enumerate function and a list comprehension:
my_list = [[1, 3, 4], [3, 6, 7], [8, 0, -1, 3]]
def func(my_list, x):
return [[idx, sublist.index(x)] for idx, sublist in enumerate(my_list)]
answer = func(my_list, 3)
print(answer)
Output
[[0, 1], [1, 0], [2, 3]]
Easy way,
def get_val(mylist, val):
for ix, item in enumerate(mylist):
yield [ix, item.index(val)]
mylist = [[1, 3, 4], [3, 6, 7], [8, 0, -1, 3]]
val = list(get_val(mylist, 3))
print(val)
Output:
[[0, 1], [1, 0], [2, 3]]