def run_algorithm(self, num_solution_before_stop=100000, time_out=1000000): """ This is where we implement our logic and algorithms Useful Parameters: self.P -- max weight we can carry self.M -- max purchasing power in dollars self.N -- total number of avaliable items self.C -- total number of constraints self.items -- all items avaliable for choice self.constraints -- a Constraint class with constraints """ # STEP: Create a hashmap from class number to its items item_map = dict() for item in self.items: if item.classNumber not in item_map: item_map[item.classNumber] = set() item_map[item.classNumber].add(item) # STEP: Calculate the total weight, cost, value, and profit of each class def get_class_stats(items): total_weight = 0 total_cost = 0 total_value = 0 total_profit = 0 for item in items: total_weight += item.weight total_cost += item.cost total_value += item.value total_profit += item.profit return (total_weight, total_cost, total_value, total_profit) class_stats = dict() # Format: key: class -> value: (weight, cost, value, profit) for classNumber in item_map.keys(): class_stats[classNumber] = get_class_stats(item_map[classNumber]) # STEP: Create a BAG instance bag = Bag(self.P, self.M, self.constraints) # STEP: PriorityQueues of class's values fn_extract_profit_per_weight_ratio = lambda x: x.profit_per_weight_ratio() def fn_extractclass_ratio(x): weight, _, _, profit = class_stats[x] if weight == 0: ratio = float("inf") else: ratio = profit / weight return ratio class_queue = PriorityQueue(lowest_priority=False) # based on class's item profit_per_weight_ratio for classNumber in item_map.keys(): class_queue.push(classNumber, fn_extractclass_ratio(classNumber)) def add_to_queue(items, fn_extract_priority, queue): for item in items: priority_value = fn_extract_priority(item) queue.push(item, -priority_value) return queue def get_queue_of_items(items, fn_extract_priority): queue = PriorityQueue(lowest_priority=False) return add_to_queue(items, fn_extract_priority, queue) # STEP: pick from the bag with highest ratio solutions_found = dict() num_solution_found = 0 iteration = 0 class_not_used_due_to_conflict = Queue() add_back_conflicts = True while num_solution_found <= num_solution_before_stop and iteration <= time_out: while not class_queue.isEmpty() and iteration <= time_out: iteration += 1 if iteration % (time_out / 1000) == 0: print("iteration {0} -- rate: {1:.2f} %".format(iteration, iteration / time_out * 100), end="\r") if not class_not_used_due_to_conflict.isEmpty(): class_to_use = class_not_used_due_to_conflict.pop() add_back_conflicts = not add_back_conflicts else: class_to_use = class_queue.pop() add_back_conflicts = not add_back_conflicts if bag.can_take(class_to_use): items_queue = get_queue_of_items(item_map[class_to_use], \ fn_extract_profit_per_weight_ratio) item = items_queue.pop() while bag.take(item): if not items_queue.isEmpty(): item = items_queue.pop() else: break num_solution_found += 1 solutions_found[bag.score()] = bag.items() print("solution {0} found".format(num_solution_found)) else: class_not_used_due_to_conflict.push(class_to_use) if num_solution_found >= num_solution_before_stop: break # print("iteration {0}".format(iteration)) iteration += 1 if add_back_conflicts: add_to_queue(class_not_used_due_to_conflict.list, fn_extractclass_ratio, class_queue) if num_solution_found >= num_solution_before_stop: break # STEP: return the best combination found bestSolution = [] bestProfit = 0 for profit, soln in solutions_found.items(): if profit > bestProfit: bestProfit = profit bestSolution = soln return bestSolution