I want to build a function that can look at the raw material on hand information I provide, then come up with combinations of inputs to achieve a desired number of finished products. on_hand_inventory = { 60: 370, 66: 180, 70: 752, 74: 97, 76: 5, 77: 11, 80: 40 } The keys of the on_hand_inventory dictionary are the product's length in feet, and the values is the number of units on-hand. I want to make finished products that are 1600 feet long, and I want to make 60 finished products. If this isn't possible, I want to know the combinations to use per finished product to get the most out of my inventory. This is the code I have so far: from itertools import combinations_with_replacement from collections import Counter on_hand_inventory = { 60: 370, 66: 180, 70: 752, 74: 97, 76: 5, 77: 11, 80: 40 } min_length = min([k for k,v in on_hand_inventory.items()]) target_length = 1600 num_finished_products_need = 60 product_lengths = sorted(on_hand_inventory.keys(), reverse=True) max_pieces = 1600//min_length # Step 1: Generate valid combinations def find_combinations(product_lengths, target_length, max_pieces): valid_combos = [] for num_pieces in range(max_pieces + 1): for combo in combinations_with_replacement(product_lengths, num_pieces): if sum(combo) == target_length: valid_combos.append(Counter(combo)) return valid_combos valid_combos = find_combinations(product_lengths, target_length, max_pieces) # Step 3: Select 60 combinations without exceeding inventory import random def select_combinations(valid_combos, on_hand_inventory, num_finished_products_need, max_attempts=10000): best_selected = [] best_used_inventory = Counter() for attempt in range(max_attempts): random.shuffle(valid_combos) selected_combos = [] used_inventory = Counter() for combo in valid_combos: temp_inventory = used_inventory + combo if all(temp_inventory[length] <= on_hand_inventory[length] for length in on_hand_inventory): selected_combos.append(combo) used_inventory += combo if len(selected_combos) == num_finished_products_need: return selected_combos, used_inventory # full success # Save the best partial result if len(selected_combos) > len(best_selected): best_selected = selected_combos best_used_inventory = used_inventory return best_selected, best_used_inventory # best attempt selected_combos, inventory_used = select_combinations(valid_combos,on_hand_inventory,num_finished_products_need,max_attempts=1000) # Step 4: Format the output for i, combo in enumerate(selected_combos, 1): parts = [f"{count} pieces of {length}'" for length, count in sorted(combo.items())] print(f"String {i}: " + ", ".join(parts)) # Step 5: Print remaining quantities by length if inventory_used: print("\nRemaining quantities by length:") for length in sorted(on_hand_inventory): remaining = on_hand_inventory[length] - inventory_used.get(length, 0) print(f"{length}': {remaining} pieces remaining") if inventory_used: print("\nUsed quantities by length:") for length in sorted(inventory_used): print(f"{length}': {inventory_used[length]} pieces remaining") This code works somewhat; however, it stops before using obvious combinations. For example, the code consistently returns a similar version of the following: 38 finished products made Remaining quantities by length: 60': 65 pieces remaining 66': 13 pieces remaining 70': 467 pieces remaining 74': 0 pieces remaining 76': 0 pieces remaining 77': 1 pieces remaining 80': 0 pieces remaining As you can see there is plenty of 60' and 70' product left to produce more finished products. Combining 1x 60' and 22x 70' would make 22 more finished products than the function returned. Any advice is welcome! Here is the full output of the Python code: Finished Product #1: 1 pieces of 66', 12 pieces of 70', 5 pieces of 76', 2 pieces of 77', 2 pieces of 80' Finished Product #2: 1 pieces of 60', 6 pieces of 66', 13 pieces of 70', 1 pieces of 74', 2 pieces of 80' Finished Product #3: 2 pieces of 60', 9 pieces of 66', 2 pieces of 70', 9 pieces of 74', 1 pieces of 80' Finished Product #4: 5 pieces of 70', 2 pieces of 74', 6 pieces of 77', 8 pieces of 80' Finished Product #5: 2 pieces of 60', 6 pieces of 66', 3 pieces of 70', 2 pieces of 77', 9 pieces of 80' Finished Product #6: 11 pieces of 60', 2 pieces of 66', 3 pieces of 70', 7 pieces of 74', 1 pieces of 80' Finished Product #7: 15 pieces of 60', 1 pieces of 66', 8 pieces of 70', 1 pieces of 74' Finished Product #8: 5 pieces of 60', 4 pieces of 66', 3 pieces of 70', 9 pieces of 74', 2 pieces of 80' Finished Product #9: 2 pieces of 60', 1 pieces of 66', 4 pieces of 70', 11 pieces of 74', 4 pieces of 80' Finished Product #10: 3 pieces of 60', 6 pieces of 66', 9 pieces of 70', 1 pieces of 74', 4 pieces of 80' Finished Product #11: 5 pieces of 60', 14 pieces of 70', 4 pieces of 80' Finished Product #12: 17 pieces of 60', 1 pieces of 66', 4 pieces of 70', 1 pieces of 74', 2 pieces of 80' Finished Product #13: 2 pieces of 60', 3 pieces of 66', 14 pieces of 70', 3 pieces of 74', 1 pieces of 80' Finished Product #14: 15 pieces of 60', 2 pieces of 66', 6 pieces of 70', 2 pieces of 74' Finished Product #15: 6 pieces of 60', 8 pieces of 66', 7 pieces of 70', 3 pieces of 74' Finished Product #16: 3 pieces of 60', 15 pieces of 70', 5 pieces of 74' Finished Product #17: 22 pieces of 60', 2 pieces of 66', 2 pieces of 74' Finished Product #18: 4 pieces of 60', 14 pieces of 66', 2 pieces of 70', 4 pieces of 74' Finished Product #19: 8 pieces of 60', 2 pieces of 66', 12 pieces of 70', 2 pieces of 74' Finished Product #20: 8 pieces of 60', 16 pieces of 70' Finished Product #21: 11 pieces of 60', 10 pieces of 66', 4 pieces of 70' Finished Product #22: 13 pieces of 60', 6 pieces of 66', 5 pieces of 70', 1 pieces of 74' Finished Product #23: 22 pieces of 60', 1 pieces of 66', 2 pieces of 70', 1 pieces of 74' Finished Product #24: 10 pieces of 60', 9 pieces of 70', 5 pieces of 74' Finished Product #25: 8 pieces of 60', 1 pieces of 66', 14 pieces of 70', 1 pieces of 74' Finished Product #26: 8 pieces of 60', 3 pieces of 66', 10 pieces of 70', 3 pieces of 74' Finished Product #27: 5 pieces of 60', 3 pieces of 66', 2 pieces of 70', 13 pieces of 74' Finished Product #28: 1 pieces of 60', 22 pieces of 70' Finished Product #29: 5 pieces of 60', 8 pieces of 70', 10 pieces of 74' Finished Product #30: 13 pieces of 60', 5 pieces of 66', 7 pieces of 70' Finished Product #31: 20 pieces of 60', 5 pieces of 66', 1 pieces of 70' Finished Product #32: 20 pieces of 66', 4 pieces of 70' Finished Product #33: 6 pieces of 60', 5 pieces of 66', 13 pieces of 70' Finished Product #34: 2 pieces of 60', 15 pieces of 66', 7 pieces of 70' Finished Product #35: 15 pieces of 60', 10 pieces of 70' Finished Product #36: 9 pieces of 60', 15 pieces of 66', 1 pieces of 70' Finished Product #37: 4 pieces of 60', 10 pieces of 66', 10 pieces of 70' Finished Product #38: 22 pieces of 60', 4 pieces of 70' Remaining quantities by length: 60': 65 pieces remaining 66': 13 pieces remaining 70': 467 pieces remaining 74': 0 pieces remaining 76': 0 pieces remaining 77': 1 pieces remaining 80': 0 pieces remaining Continue reading...