diff --git a/greedy_method/greedy_knapsack.py b/greedy_method/greedy_knapsack.py new file mode 100644 index 000000000..92dd81aaa --- /dev/null +++ b/greedy_method/greedy_knapsack.py @@ -0,0 +1,99 @@ +# To get an insight into Greedy Algorithm through the Knapsack problem + + +""" +A shopkeeper has bags of wheat that each have different weights and different profits. +eg. +profit 5 8 7 1 12 3 4 +weight 2 7 1 6 4 2 5 +max_weight 100 + +Constraints: +max_weight > 0 +profit[i] >= 0 +weight[i] >= 0 +Calculate the maximum profit that the shopkeeper can make given maxmum weight that can +be carried. +""" +from typing import Union + + +def calc_profit(profit: list, weight: list, max_weight: int) -> Union[str, int]: + """ + Function description is as follows- + :param profit: Take a list of profits + :param weight: Take a list of weight if bags corresponding to the profits + :param max_weight: Maximum weight that could be carried + :return: Maximum expected gain + + >>> calc_profit([1, 2, 3], [3, 4, 5], 15) + 6 + >>> calc_profit([10, 9 , 8], [3 ,4 , 5], 25) + 27 + """ + if len(profit) != len(weight): + raise ValueError("The length of profit and weight must be same.") + if max_weight <= 0: + raise ValueError("max_weight must greater than zero.") + if any(p < 0 for p in profit): + raise ValueError("Profit can not be negative.") + if any(w < 0 for w in weight): + raise ValueError("Weight can not be negative.") + + # List created to store profit gained for the 1kg in case of each weight + # respectively. Calculate and append profit/weight for each element. + profit_by_weight = [p / w for p, w in zip(profit, weight)] + + # Creating a copy of the list and sorting profit/weight in ascending order + sorted_profit_by_weight = sorted(profit_by_weight) + + # declaring useful variables + length = len(sorted_profit_by_weight) + limit = 0 + gain = 0 + i = 0 + + # loop till the total weight do not reach max limit e.g. 15 kg and till i= weight[index]: + limit += weight[index] + # Adding profit gained for the given weight 1 === + # weight[index]/weight[index] + gain += 1 * profit[index] + else: + # Since the weight encountered is greater than limit, therefore take the + # required number of remaining kgs and calculate profit for it. + # weight remaining / weight[index] + gain += (max_weight - limit) / weight[index] * profit[index] + break + i += 1 + return gain + + +if __name__ == "__main__": + print( + "Input profits, weights, and then max_weight (all positive ints) separated by " + "spaces." + ) + + profit = [int(x) for x in input("Input profits separated by spaces: ").split()] + weight = [int(x) for x in input("Input weights separated by spaces: ").split()] + max_weight = int(input("Max weight allowed: ")) + + # Function Call + calc_profit(profit, weight, max_weight) diff --git a/greedy_method/test_knapsack.py b/greedy_method/test_knapsack.py new file mode 100644 index 000000000..8f8107bc7 --- /dev/null +++ b/greedy_method/test_knapsack.py @@ -0,0 +1,74 @@ +import unittest +import greedy_knapsack as kp + + +class TestClass(unittest.TestCase): + """ + Test cases for knapsack + """ + + def test_sorted(self): + """ + kp.calc_profit takes the required argument (profit, weight, max_weight) + and returns whether the answer matches to the expected ones + """ + profit = [10, 20, 30, 40, 50, 60] + weight = [2, 4, 6, 8, 10, 12] + max_weight = 100 + self.assertEqual(kp.calc_profit(profit, weight, max_weight), 210) + + def test_negative_max_weight(self): + """ + Returns ValueError for any negative max_weight value + :return: ValueError + """ + # profit = [10, 20, 30, 40, 50, 60] + # weight = [2, 4, 6, 8, 10, 12] + # max_weight = -15 + self.assertRaisesRegex(ValueError, "max_weight must greater than zero.") + + def test_negative_profit_value(self): + """ + Returns ValueError for any negative profit value in the list + :return: ValueError + """ + # profit = [10, -20, 30, 40, 50, 60] + # weight = [2, 4, 6, 8, 10, 12] + # max_weight = 15 + self.assertRaisesRegex( + ValueError, "Weight can not be negative.", + ) + + def test_negative_weight_value(self): + """ + Returns ValueError for any negative weight value in the list + :return: ValueError + """ + # profit = [10, 20, 30, 40, 50, 60] + # weight = [2, -4, 6, -8, 10, 12] + # max_weight = 15 + self.assertRaisesRegex(ValueError, "Profit can not be negative.") + + def test_null_max_weight(self): + """ + Returns ValueError for any zero max_weight value + :return: ValueError + """ + # profit = [10, 20, 30, 40, 50, 60] + # weight = [2, 4, 6, 8, 10, 12] + # max_weight = null + self.assertRaisesRegex(ValueError, "max_weight must greater than zero.") + + def test_unequal_list_length(self): + """ + Returns IndexError if length of lists (profit and weight) are unequal. + :return: IndexError + """ + # profit = [10, 20, 30, 40, 50] + # weight = [2, 4, 6, 8, 10, 12] + # max_weight = 100 + self.assertRaisesRegex(IndexError, "The length of profit and weight must be same.") + + +if __name__ == "__main__": + unittest.main()