Solving 0-1 knapsack problem using memoization - python

Although I managed to get the correct output for a 0-1 knapsack problem, I'm still partially confused about how the code functions.
This is my code:
memo = {}
def knapsack(weights, prices, capacity, i, memo):
if capacity <= 0 or i >= len(weights):
return 0
if (capacity, i) in memo:
return memo[(capacity, i)]
if weights[i] > capacity:
memo[(capacity, i)] = knapsack(weights, prices, capacity, i + 1, memo)
return memo[(capacity, i)]
memo[(capacity, i)] = max(prices[i] + knapsack(weights, prices, capacity - weights[i], i + 1, memo), knapsack(weights, prices, capacity, i + 1, memo))
return memo[(capacity, i)]
print(knapsack([1,2,4,6], [4,2,4,7], 7, 0, memo))
When using memoization, the data for previously solved cases are stored in memo, however, there are multiple cases with the same key-(capacity, i) but different outputs
Take for example c=1, i=3 (the 2 yellow circles)
Hence my question, won't the data with the same key in memo overwrite each other and thus affecting the optimal output?
I tried to print out the memo dict but still couldn't understand what's going on.
memo = {
(4, 3): 0,
(4, 2): 4,
(2, 3): 0,
(6, 3): 7,
(6, 2): 7,
(6, 1): 7,
(1, 3): 0,
(5, 3): 0,
(5, 2): 4,
(3, 3): 0,
(7, 3): 7,
(7, 2): 7,
(7, 1): 7,
(7, 0): 11,
}

Related

How to sort a dictionary by values without using in-built sorted function?

I want to implement this without using the sorted function (perhaps by using a priority queue):
nums = [2,3,1,3,2]
dic=collections.Counter(nums)
sorted_dic= dict(sorted(dic.items(),key=lambda x: x[1]))
I have written a custom sort function to sort array/dictionary in ascending/descending order.
def bubble_sort(arr): #You can use any sort - for the sake of simplicity I have used bubble sort
for i in range(len(arr)-1):
for j in range(len(arr)-1-i):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
def my_custom_sort(obj,key=None,reverse=False):
if type(obj)==dict:
if key==0:
arr = list(map(lambda x:x[0], obj.items())) #fetch the list of keys
else:
dic = {y:x for x,y in obj.items()} #reverse the key value pairs, so that it is easy to match key/values
arr = list(map(lambda x:x[0], dic.items()))
else:
arr=obj
sorted_arr = bubble_sort(arr)
if reverse: sorted_arr =sorted_arr[::-1]
if type(obj)==dict:
if key==0:
res = [(key,obj[key]) for key in sorted_arr]
return res
else:
return [(dic[key],key) for key in sorted_arr]
return res
else:
return sorted_arr
arr=[4,2,6,7,1]
dic={3:1,2:4,1:0,0:8}
print(my_custom_sort(arr)) #sort array in increasing order
print(my_custom_sort(arr,reverse=True)) #sort array in decreasing order
print(my_custom_sort(dic,key=0)) #sort dictionary by key in inreasing order
print(my_custom_sort(dic,key=1)) #sort dictionary by values in increasing order
print(my_custom_sort(dic,key=0,reverse=True)) #sort dictionary by key in decreasing order
print(my_custom_sort(dic,key=1,reverse=True)) #sort dictionary by values in decreasing order
Output:
[1, 2, 4, 6, 7]
[7, 6, 4, 2, 1]
[(0, 8), (1, 0), (2, 4), (3, 1)]
[(1, 0), (3, 1), (2, 4), (0, 8)]
[(3, 1), (2, 4), (1, 0), (0, 8)]
[(0, 8), (2, 4), (3, 1), (1, 0)]
I hope this is helpful for someone who wants to implement his own custom sort function (if asked in the interview)

Finding a pair of elements in a list that adds up to a sum using a dictionary

Context of the problem:
Find Pair with given Sum in the Array.
Given an unsorted list of ints, find a pair with a given sum in it.
EXAMPLE:
list = [8, 7, 2, 5, 3, 1]
sum = 10
OUTPUT = index 0 & 2 (8, 2) or 1 & 4 (7, 3)
This is what I have so far:
def find_pair_dict(ints: [int], sum_: int):
dict_ = dict()
# {
# element: index
# 8: 0,
# 7: 1,
# ...
# }
output = list()
for i in range(len(ints)):
diff = sum_ - ints[i]
# print(diff)
if diff not in dict_.keys():
# int: index
dict_[ints[i]] = i
else:
output.append((dict_[ints[i]], dict_[diff]))
if not output:
return "No pairs were found"
return output
I am calling this function with find_pair_dict([8, 7, 2, 5, 3, 1], 10) and am getting an error that I do not understand.
The Error
Traceback (most recent call last):
File "find_pair_sum.py", line 62, in <module>
print(find_pair_dict([8, 7, 2, 5, 3, 1], 10))
File "find_pair_sum.py", line 53, in find_pair_dict
output.append((dict_[ints[i]], dict_[diff]))
KeyError: 2
This sounds like the element of 2 cannot be added?
Almost a one-liner:
def find_pairs(ints: [int], sum_: int):
return {
tuple(sorted((n, ints.index(sum_-i)))): (i, sum_-i)
for n, i in enumerate(ints) if sum_ - i in ints
}
print(find_pairs([8, 7, 2, 5, 3, 1], 10))
Result:
{(0, 2): (2, 8), (1, 4): (3, 7), (3, 3): (5, 5)}
Note: the key to the dictionary is a sorted tuple. A tuple because a list isn't hashable and sorted to avoid both (0,2) and (2,0) showing up as keys (for example).
The error in your solution occurs because the first time this line is executed:
output.append((dict_[ints[i]], dict_[diff]))
The value if dict_ is {8: 0, 7: 1} and ints[i] is 2. Since there is no entry for 2 in the dict, you get this error.
Actually I think there should be three outputs:
(2,0) = 2,8
(4,1) = 3,7
(3,3) = 5,5
The problem has already been succinctly described by #Selcuk. I am suggesting you how to solve that. Many ways. Easiest for me is to use defaultdict from collections.
Below is the code with minimal changes
from collections import defaultdict
def find_pair_dict(ints: [int], sum_: int):
dict_ = defaultdict(list)
output = list()
for i in range(len(ints)):
diff = sum_ - ints[i]
dict_[ints[i]] = i
output.append((dict_[ints[i]], dict_[diff]))
if not output:
return "No pairs were found"
return output
print(find_pair_dict([8, 7, 2, 5, 3, 1], 10))
This prints out the following. I have not filtered for empty matches.
[(0, []), (1, []), (2, 0), (3, 3), (4, 1), (5, [])]
Is this what you wanted?

quick way to do def pair_sum()

Does anyone know how to do this in a simple and effective way?
Thanks
Define a function called pair_sum() which takes two inputs: a list of integers and a total.
The function should return a list of tuples, where each value in the tuple is a unique value from the input list, and where the sum of the tuple elements equals the total. Each pair of values in the input list that sums to the total should only appear once in the output list. For example, if the input list is [3, 2, 1] and the total is 4, then the output list will only contain the tuple (3, 1) and not the tuple (1, 3). In other words, if (i, j) is a tuple in the output list, then i should appear to the left of j in the input list.
For example:
Test Result
print(pair_sum([4, 6, 2, 7, 3], 10))
[(4, 6), (7, 3)]
print(pair_sum([4, 7, 8, 9, 3, 2, 6, 11, 1, 5, 10], 14))
[(4, 10), (8, 6), (9, 5), (3, 11)]
#xaovnumwsercz, I proposed this version.
def pair_sum(numbers, target):
answer = []
for i, num in enumerate(numbers):
if target-num in numbers[i+1:]:
answer.append((num,target-num))
return answer
def pair_sum (numbers, pairSum):
resultSet=[];
newNumbers = sorted(numbers);
i = 0;
j = len(newNumbers)-1;
while i < len(newNumbers) and j >= 0:
if newNumbers[i] + newNumbers[j] == pairSum and i != j:
if (newNumbers[j], newNumbers[i]) not in resultSet and numbers.index(newNumbers[i]) < numbers.index(newNumbers[j]):
resultSet.append((newNumbers[i], newNumbers[j]))
numbers.remove(newNumbers[i]);
numbers.remove(newNumbers[j])
i = i + 1;
j = j - 1;
elif newNumbers[i] + newNumbers[j] < pairSum:
i = i + 1;
else:
j = j - 1;
return (resultSet);

Subset Sum with Backtracking on Python

So I want to print out all the subsets of the initial set that will add up to 21. So far I've only come up with this
def twentyone(array, num=21):
if len(array) == 0:
return None
else:
if array[0] == num:
return [array[0]]
else:
with_v = twentyone(array[1:], (num - array[0]))
if with_v:
return [array[0]] + with_v
else:
return twentyone(array[1:], num)
It does give the solution, but only the first. How do I change it so that it will give me every possible subset. I've tried making a few changes but it only gives me nested lists. Any help would be nice.
You can create a recursive generator:
def twentyone(array, num=21):
if num < 0:
return
if len(array) == 0:
if num == 0:
yield []
return
for solution in twentyone(array[1:], num):
yield solution
for solution in twentyone(array[1:], num - array[0]):
yield [array[0]] + solution
Example:
>>> list(twentyone([5, 16, 3, 2]))
[[16, 3, 2], [5, 16]]
If you are allowed to use standard Python libraries, here is a shorter solution:
import itertools
import operator
def twentyone(array, num=21):
subsets = reduce(operator.add, [list(itertools.combinations(array, r)) for r in range(1, 1 + len(array))])
return [subset for subset in subsets if sum(subset) == num]
print twentyone([1, 2, 5, 6, 8, 9, 10])
result:
[(2, 9, 10), (5, 6, 10), (1, 2, 8, 10), (1, 5, 6, 9), (2, 5, 6, 8)]

How do i find the percentage of the most common element in a list?

I have been recently using Counter().most_common but the problem is that I need to turn the bit where it shows how much it came up into a percentage, for example:
[(2, 5), (10, 5)]
to:
[(2, 50%), (10, 50%)]
Is there any way of doing this using Counter().most_common, or any other method?
Here is part of my code:
while count < int(DR):
count = count + int(1)
DV.append(random.randint(1, int(DI)))
if count == int(DR):
print ('\n(The Number that was rolled , the amount of times it came up):')
global x
print (Counter(DV).most_common(int((DI))))
from collections import Counter
l = [1, 1, 2, 2, 2, 2, 2, 3, 4, 10, 10, 10, 10, 10]
c = Counter(l)
[(i, c[i] / len(l) * 100.0) for i in c]
Output, in the form (element, % of total)
[(1, 14.285714285714285),
(2, 35.714285714285715),
(3, 7.142857142857142),
(4, 7.142857142857142),
(10, 35.714285714285715)]
To list them in order you can use collections.Counter.most_common
>>> [(i, c[i] / len(l) * 100.0) for i, count in c.most_common()]
[(2, 35.714285714285715),
(10, 35.714285714285715),
(1, 14.285714285714285),
(3, 7.142857142857142),
(4, 7.142857142857142)]
If you don't have the original data, you can still accomplish this just with the Counter.
OrderedDict([(i, str(round(count / sum(c.values()) * 100.0, 3)) + '%') for i, count in c.most_common()])
Where:
i is the item that was counted;
count is the count of that item;
c is the Counter object
3 is the precision of the percentage
Performance can be improved if sum(c.values()) is moved outside of the list compression.

Categories