I have a box with a maximum weight capacity. I am given a list of weights of the items that I am supposed to fit in the box. I need all solutions that get to, or as close as possible to, the maximum capacity. What I mean is that I should not be able to add another item to any of the partial solutions without going over the maximum capacity.
If I am given the following list of weights:
[1,2,3,4,5]
If the maximum capacity is 9, the solutions should be (I may have missed one, but you get the point):
[[4,3,2], [5,3,1], [5,4], [3,2,1], [4,2,1], [4,3,1], [5,3]]
Here's my recursive algorithm, I think I am getting close but I can't figure out how to fix it.
def findSubset(alist, maxim):
if maxim <= 0:
return [[]]
if len(alist) == 0:
return []
alist2 = alist[1:]
include = findSubset(alist2, maxim-alist[0])
for s in include:
s.append(alist[0])
return include + findSubset(alist2, maxim)
Current output is:
[[4, 3, 2, 1], [5, 3, 2, 1], [5, 4, 2, 1], [5, 4, 3, 1], [5, 3, 1], [5, 4, 1], [4, 3, 2], [5, 3, 2], [5, 4, 2], [5, 4, 3], [5, 4]]
My advice would be to iterate on all element of the list, while building a list of possible solutions
if the element is < maxim, recurse on a sublist on following elements for maxim - elt, and for each element of the result, append the element to it and append all to the resulting list
if the element is == maxim, add the singleton list containing the element to the resulting list
Code is:
def findSubset(alist, maxim):
res = []
if maxim <= 0:
return [[]]
if len(alist) == 0:
return []
for i, elt in enumerate(alist):
if elt < maxim:
res.extend([ [elt] + l for l in findSubset(alist[i+1:], maxim-elt)])
elif elt == maxim:
res.append([elt])
return res
It gives
>>> findSubset(lst, 9)
[[1, 3, 5], [2, 3, 4], [4, 5]]
Above code only gives exact solutions. In case where there are no exact solution, it should be extended to give best approaching solutions:
def findApproachSubset(alist, maxim):
for i in range(maxim, 0, -1):
res = findSubset(alist, i)
if len(res) > 0:
return res
return []
For example:
>>> findSubset([1, 4, 5, 6], 8)
[]
>>> findApproachSubset([1, 4, 5, 6], 8)
[[1, 6]]
because here the best solution is for 7 instead of 8.
This is written using Haskell. As it seems that this is homework, there is no point giving you the complete answer.
We use three separate functions, the function f can be made recursive as it is just map
xs = [1, 2, 3, 4, 5]
perms [] = []
perms xs = xs : perms (tail xs)
All you need to know is that : means cons i.e 1:[2] = [1,2]
In python, we could write it as:
def perms(xs):
if xs:
return [xs] + perms(xs[1:])
else:
return []
so perms xs is just [[1,2,3,4,5],[2,3,4,5],[3,4,5],[4,5],[5]]
takeWhileLessThan _ [] = []
takeWhileLessThan n (x:xs) =
if n-x < 0 then [] else (x : takeWhileLessThan (n-x) xs)
and takeWhileLessThan uses recursion. It operates on a single list, keeping track of the current sum.
f n xs = map (takeWhileLessThan n) (perms xs)
> [[1,2,3],[2,3,4],[3,4],[4,5],[5]]
The final function maps the recursive function over the list of lists. If you then wanted all values and wanted it to be recursive, just write another function...
if you are allowed for loops, then this method would work well. The only recursion is the main function.
def f(n, xs, solutions):
if xs:
m = n
ys = []
for x in xs:
if (m-x) < 0:
break
else:
ys.append(x)
m = m-x
solutions.append(ys)
return f(n, xs[1:], solutions)
else:
return solutions
which gives:
>>> f(9, [1,2,3,4,5], [])
[[1, 2, 3], [2, 3, 4], [3, 4], [4, 5], [5]]
def recursive_function(current_node: int, nodes_left: list, maximum: int, trace: list) -> list:
sum_so_far = sum(trace)
if current_node is not None:
trace.append(current_node)
else:
current_node = 0
if sum_so_far + current_node > maximum:
# That means, that current trace is already to big
# no need to search further this path
return False
elif sum_so_far + current_node == maximum:
# Bingo! The perfect set of nodes!
# No need to look further this path
return {tuple(sorted(trace))}
else:
# We still haven't reached maximum value
# let's check if we can pack some more nodes to trace
results = set()
for node in nodes_left:
nodes_copy = nodes_left.copy()
nodes_copy.remove(node)
traces = recursive_function(node, nodes_copy, maximum, trace.copy())
if traces:
# This path gave us some legitimate results, we collect them
results.update(traces)
if results:
# At least one possible path gave us results, we pass them on
return results
# There was nothing left in nodes_left that we could add and not
# cross the maximum limit. The path leading to this moment fits
# our requirements.
return {tuple(sorted(trace))}
def findSubset(alist, maxim) -> list:
results = recursive_function(None, alist, maxim, [])
return results
Why do I do this {tuple(sorted(trace))}? So I don't keep different permutations of same results in my memory.
Time cost: n!
Memory cost: n
Related
Let us say we have a list and target of:
list: [1,2,3,4,5] and target: 20
and we want to find total combinations to reach this, with multiplication, which is:
[1,4,5], [4,5], [2,5,2], [1,2,2,5]
I did this code, but I can't seem to know how to remove the ones so I have them too, I mean that I receive:
[1,4,5], [1,2,2,5].
But without [1], I can't seem to get it, I tried to "cheat" somehow to get it, but I can't since my code doesn't fit for it...
def Coin(target, lst, temp=[], comb=[]):
if target == 1:
comb.append(temp)
return comb
if len(lst) == 0:
return []
if target >= lst[0]:
if lst[0] > 1:
take=Coin(target/lst[0],lst,temp+[lst[0]],comb)
dont_take=Coin(target,lst[1:],temp,comb)
else:
take_1 = Coin(target, lst[1:], temp + [1], comb)
return take_1
return take
return comb
print(Coin(20, [1,2,3,4,5], [], []))
[[1, 2, 2, 5], [1, 4, 5]]
How to add the parts without 1? I don't need a solution, since as said, not homework, but practice for exam. Just a clue will be enough, I want to find it myself, but I need a clue for it.
Maybe you should combine
if lst[0] > 1:
else:
together, that means for 1 we should also decide whether take it or not.
This is much easier to do with a generator using yield than a function using return.
The difference is that you can only return once, while you can yield any number of times (including 0 if there are no solutions).
If you wish to return a list, you still can, like this:
def coin (target, lst):
return list(_coin(target, lst, 0)
def _coin (target, lst, i):
...
If that doesn't matter, the generator saves memory by not having to generate the whole list at once. And you simply:
def coin (target, lst, i=0):
... # Use yield as often as you want, all will be returned.
Second, you are running into the most common gotcha in Python. You are using mutable objects as defaults. If you're going to continue with your current approach you need to:
def coin(target, lst, temp=None, comb=None):
if temp is None:
temp = []
if comb is None:
comb = []
...
And third, you should get in the habit of following standard style conventions. In many ways, what the convention is doesn't matter that much. But that everyone is on the same page, does. Therefore you should try to follow the most common Python convention. In which the function should be named coin instead of Coin.
Edit:
The rules for the question is:
Positive integers only ( 0 not allowed ).
Number can appear once only ( at input ).
can not repeat numbers on list.
EG: [1,2,3,4], n=12 >> [1,12], [12,1], [3,4], [2,6], [1,3,4], [1,2,6].
NOT: [1,2,2,3], [2,2,3]
That is all I guess.
def coin_change_MULTI(num, lst):
if num == 1 and 1 not in lst:
return []
return Coin_MULTI(num, sorted(lst), [], [])
def Coin_MULTI(target, lst, temp=[], comb=[]):
if target == 1:
if big_than_target(1, lst):
return [[1]]
comb.append(temp)
return comb
if len(lst) == 0: return []
if target >= lst[0]:
if lst[0] > 1:
take=Coin_MULTI(target/lst[0],lst[1:],temp+[lst[0]],comb)
dont_take=Coin_MULTI(target,lst[1:],temp,comb)
return comb
else:
take_1 = Coin_MULTI(target, lst[1:], temp + [1], comb)
dont_take_1 = Coin_MULTI(target, lst[1:], temp, comb)
return comb
return take + dont_take
return comb
print(coin_change_MULTI(12, [2,4,6,12,7,3, 1]))
print(coin_change_MULTI(1, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(1, [2,4,6,12,7,3]))
print(coin_change_MULTI(100, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(576, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(12096, [2,4,6,12,7,3,1]))
print(coin_change_MULTI(0, [2,4,6,12,7,3,1]))
print((coin_change_MULTI(24, [2,4,6,12,7,3,1])))
[[1, 2, 6], [1, 3, 4], [1, 12], [2, 6], [3, 4], [12]]
[[1]]
[]
[]
[[1, 2, 4, 6, 12], [2, 4, 6, 12]]
[[1, 2, 3, 4, 6, 7, 12], [2, 3, 4, 6, 7, 12]]
[]
[[1, 2, 3, 4], [1, 2, 12], [1, 4, 6], [2, 3, 4], [2, 12], [4, 6]]
Process finished with exit code 0
I'm trying to find the next maximum value of nested lists, I already have a nested list sorted by bubblesort, I need to take the largest element of each nested list and insert it into the solution vector, until the solution vector is sorted.
P.S: I can't delete the element from the initial nested list, only find the next maximum value.
See the image at the bottom as an example:
Nested_list = [[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
The way I devised deleted the largest vector from the original list, which was quite time consuming, I believe that just moving the index to the next largest value will consume less time:
def bubbleSort(array):
n = len(array)-1
for i in range(n):
for j in range(0, n-i):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
else:
continue
return array
def ordena_lista(output):
for sublista in output:
bubbleSort(sublista)
def maior_valor_lista(output):
return list(el[-1] for el in output)
def nested_remove(L, x):
if x in L:
L.remove(x)
else:
for element in L:
if type(element) is list:
nested_remove(element, x)
b = list(random.sample(range(10), 10))
n= m.floor(m.sqrt(len(b)))
output=list([b[i:i + n] for i in range(0, len(b), n)])
ordena_lista(b)
while output:
valores_maximo = maior_valor_lista(output)
var = max(valores_maximo, key=int)
final = [var] + final
nested_remove(output, var)
output = list(filter(None, output))
The simplest solution would be the following,
from functools import reduce
from operator import add
def sort_nested_list(nested_list):
return sorted(reduce(add, nested_list))
but, without knowing the exact implementation details of python's sorted, I can't tell you if it takes advantage of your pre-sorting.
If we know the sublists are sorted, and we are allowed to copy the list, and we know how many elements there are in total, we can write the following,
import math
from copy import deepcopy
def get_max_and_pop(nested_list):
""" find the maximum element of a sublist of nested_list, remove it from the sublist, and return it """
print(f"get_max_and_pop says: {nested_list}")
return max(nested_list, key=lambda x: x[-1:]).pop()
def sort_nested_list_whose_sublists_are_sorted(nested_list, n_elements):
nested_list_copy = deepcopy(nested_list)
return [get_max_and_pop(nested_list=nested_list_copy) for _ in range(n_elements)][::-1]
edit: without knowledge of the number of elements, we can write,
from copy import deepcopy
def sort_nested_list_whose_sublists_are_sorted_iter(nested_list):
nested_list_copy = deepcopy(nested_list)
while any(nested_list_copy):
yield max(nested_list_copy, key=lambda x: x[-1:]).pop()
This amounts to a bizarre, woefully inefficient and completely unnecessary sorting algorithm but here goes anyway:
Nested_list = [[9, 7, 1], [4, 5, 6], [2, 3, 8], [0]]
for e in Nested_list:
e.sort()
Output_list = []
Nested_list_copy = [[e_ for e_ in e] for e in Nested_list]
element_count = sum(len(e) for e in Nested_list)
for _ in range(element_count):
m = None
for i, e in enumerate(Nested_list_copy):
if e:
tm = e[-1]
if m is None or tm > m:
m = tm
k = i
Output_list.insert(0, Nested_list_copy[k].pop())
print(Nested_list)
print(Output_list)
Output:
[[1, 7, 9], [4, 5, 6], [2, 3, 8], [0]]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I am trying to make a program that checks whether which items are equal to a target value in a list and then output their indexes.
E.g.
li = [2, 5, 7, 9, 3]
target = 16
output: [2, 3]
li = [2, 5, 7, 9, 3]
target = 7
output: [0, 1]
Another way, assuming you can sort the list is the following
original_l = [1,2,6,4,9,3]
my_l = [ [index, item] for item,index in zip(original_l, range(0,len(original_l)))]
my_l_sort = sorted(my_l, key=lambda x: x[1])
start_i = 0
end_i = len(my_l_sort)-1
result = []
target = 7
while start_i < end_i:
if my_l_sort[start_i][1] + my_l_sort[end_i][1] == target:
result.append([my_l_sort[start_i][0], my_l_sort[end_i][0]])
break
elif my_l_sort[start_i][1] + my_l_sort[end_i][1] < target:
start_i+=1
else:
end_i-=1
if len(result) != 0:
print(f"Match for indices {result[0]}")
else:
print("No match")
The indices 0 and 1 of result[0] are respectively the 2 positions, given as a 2 element string, in original_l that holds the values that summed give the target.
is this a homework?
Anyways, here is the answer you are looking for
def check_sum(l, target):
for i in range(len(l)):
sum_temp = 0
for j in range(i, len(l)):
if sum_temp == target:
return [i, j-1]
else:
sum_temp += l[j]
return None
print(check_sum([2, 5, 7, 9, 3], 16))
"""
check_sum([2, 5, 7, 9, 3], 16)
>>> [2, 3]
check_sum([2, 5, 7, 9, 3], 7)
>>> [0, 1]
check_sum([2, 5, 7, 9, 3], 99)
>>> None
"""
The code is self-explanatory and does not require extra commenting. It simply iterates over the list of integers you have as an input and tries to find a sequence of values that add up to your target.
If you dont worry about stack explosion, for smaller input.
We divide solutions containing an index and not containing index and merge all those solution. It returns indices of all possible solutions.
It is O(2^n) solutions. Similar ones
def solve(residual_sum, original_list, present_index):
'''Returns list of list of indices where sum gives residual_sum'''
if present_index == len(original_list)-1:
# If at end of list
if residual_sum == original_list[-1]:
# if residual sum if equal to present element
# then this index is part of solution
return [[present_index]]
if residual_sum == 0:
# 0 sum, empty solution
return [[]]
# Reaching here would mean list at caller side can not
# lead to desired sum, so there is no solution possible
return []
all_sols = []
# Get all solutions which contain i
# since i is part of solution,
# so we only need to find for residual_sum-original_list[present_index]
solutions_with_i = solve(residual_sum-original_list[present_index], original_list, present_index+1)
if solutions_with_i:
# Add solutions containing i
all_sols.extend([[present_index] + x for x in solutions_with_i])
# solution dont contain i, so use same residual sum
solutions_without_i = solve(residual_sum, original_list, present_index+1)
if solutions_without_i:
all_sols.extend(solutions_without_i)
return all_sols
print(solve(16, [2, 5, 7, 9, 3], 0))
Indices
[[0, 1, 3], [2, 3]]
I'm stuck with this problem when I was doing some math and I need to find all possible sums of a given number. Here is a sample input and output for a better understanding of the problem.
Let say we are given an array arr=[1,2,3,4,5] and given number number=10 then i need something like this,
def solution(arr,number):
#you can code here
output : [1,2,3,4],[2,3,5],[1,5,4],[1,3,6],[1,6]
There are many interesting answers and discussion here and also here.
Here is a quick (tested) answer for you:
import itertools
def solution(arr, number):
for i in range(len(arr)+1):
for s in list(itertools.combinations(arr, i)):
if sum(s) == number:
yield list(s)
arr = [ 1, 2, 3, 4, 5 ]
number = 10
print(list(solution(arr, number)))
Prints:
[[1, 4, 5], [2, 3, 5], [1, 2, 3, 4]]
Solve using Dynamic Programming algorithm
def solve(arr, s, i = None, path = None, solutions = None):
"""Use Dynamic Programming to find subsequence of arr
that sum to s
Arguments:
arr - array
i - current index that will use or not for arr
path - current subsequence we are summing
solutions - all solutions found
"""
# Set defaults
if i is None:
i = len(arr) - 1
if solutions is None:
solutions = []
if path is None:
path = []
# Base Cases
if s == 0:
solutions.append(path[:])
return solutions
if i < 0 or s < 0:
return
# Try with arr[i] in path
if arr[i] <= s:
solve(arr, s-arr[i], i-1, [arr[i]] + path, solutions)
# Try with arr[i] not in path
solve(arr, s, i-1, path, solutions)
return solutions
Test
arr = [1, 2, 3, 4, 5]
print(solve(arr, 10))
Output
[[1, 4, 5], [2, 3, 5], [1, 2, 3, 4]]
So here's what I want to do: I have a list that contains several equivalence relations:
l = [[1, 2], [2, 3], [4, 5], [6, 7], [1, 7]]
And I want to union the sets that share one element. Here is a sample implementation:
def union(lis):
lis = [set(e) for e in lis]
res = []
while True:
for i in range(len(lis)):
a = lis[i]
if res == []:
res.append(a)
else:
pointer = 0
while pointer < len(res):
if a & res[pointer] != set([]) :
res[pointer] = res[pointer].union(a)
break
pointer +=1
if pointer == len(res):
res.append(a)
if res == lis:
break
lis,res = res,[]
return res
And it prints
[set([1, 2, 3, 6, 7]), set([4, 5])]
This does the right thing but is way too slow when the equivalence relations is too large. I looked up the descriptions on union-find algorithm: http://en.wikipedia.org/wiki/Disjoint-set_data_structure
but I still having problem coding a Python implementation.
Solution that runs in O(n) time
def indices_dict(lis):
d = defaultdict(list)
for i,(a,b) in enumerate(lis):
d[a].append(i)
d[b].append(i)
return d
def disjoint_indices(lis):
d = indices_dict(lis)
sets = []
while len(d):
que = set(d.popitem()[1])
ind = set()
while len(que):
ind |= que
que = set([y for i in que
for x in lis[i]
for y in d.pop(x, [])]) - ind
sets += [ind]
return sets
def disjoint_sets(lis):
return [set([x for i in s for x in lis[i]]) for s in disjoint_indices(lis)]
How it works:
>>> lis = [(1,2),(2,3),(4,5),(6,7),(1,7)]
>>> indices_dict(lis)
>>> {1: [0, 4], 2: [0, 1], 3: [1], 4: [2], 5: [2], 6: [3], 7: [3, 4]})
indices_dict gives a map from an equivalence # to an index in lis. E.g. 1 is mapped to index 0 and 4 in lis.
>>> disjoint_indices(lis)
>>> [set([0,1,3,4], set([2])]
disjoint_indices gives a list of disjoint sets of indices. Each set corresponds to indices in an equivalence. E.g. lis[0] and lis[3] are in the same equivalence but not lis[2].
>>> disjoint_set(lis)
>>> [set([1, 2, 3, 6, 7]), set([4, 5])]
disjoint_set converts disjoint indices into into their proper equivalences.
Time complexity
The O(n) time complexity is difficult to see but I'll try to explain. Here I will use n = len(lis).
indices_dict certainly runs in O(n) time because only 1 for-loop
disjoint_indices is the hardest to see. It certainly runs in O(len(d)) time since the outer loop stops when d is empty and the inner loop removes an element of d each iteration. now, the len(d) <= 2n since d is a map from equivalence #'s to indices and there are at most 2n different equivalence #'s in lis. Therefore, the function runs in O(n).
disjoint_sets is difficult to see because of the 3 combined for-loops. However, you'll notice that at most i can run over all n indices in lis and x runs over the 2-tuple, so the total complexity is 2n = O(n)
I think this is an elegant solution, using the built in set functions:
#!/usr/bin/python3
def union_find(lis):
lis = map(set, lis)
unions = []
for item in lis:
temp = []
for s in unions:
if not s.isdisjoint(item):
item = s.union(item)
else:
temp.append(s)
temp.append(item)
unions = temp
return unions
if __name__ == '__main__':
l = [[1, 2], [2, 3], [4, 5], [6, 7], [1, 7]]
print(union_find(l))
It returns a list of sets.
Perhaps something like this?
#!/usr/local/cpython-3.3/bin/python
import copy
import pprint
import collections
def union(list_):
dict_ = collections.defaultdict(set)
for sublist in list_:
dict_[sublist[0]].add(sublist[1])
dict_[sublist[1]].add(sublist[0])
change_made = True
while change_made:
change_made = False
for key, values in dict_.items():
for value in copy.copy(values):
for element in dict_[value]:
if element not in dict_[key]:
dict_[key].add(element)
change_made = True
return dict_
list_ = [ [1, 2], [2, 3], [4, 5], [6, 7], [1, 7] ]
pprint.pprint(union(list_))
This works by completely exhausting one equivalence at a time. When an element finds it's equivalence it is removed from the original set and no longer searched.
def equiv_sets(lis):
s = set(lis)
sets = []
#loop while there are still items in original set
while len(s):
s1 = set(s.pop())
length = 0
#loop while there are still equivalences to s1
while( len(s1) != length):
length = len(s1)
for v in list(s):
if v[0] in s1 or v[1] in s1:
s1 |= set(v)
s -= set([v])
sets += [s1]
return sets
print equiv_sets([(1,2),(2,3),(4,5),(6,7),(1,7)])
OUTPUT: [set([1, 2, 3, 6, 7]), set([4, 5])]