Reputation: 111
Problem Statement: I have 150 objects with weights and values attached to them. The weights of the objects might change based on the order they are selected, typically about 70-80 items are selected. I can only select up to a maximum weight, so all permutations that start with the same sequence need to be skipped once I've found a sub-solution with that sequence. The goal is to maximize value.
I can trivially create all permutations with:
from itertools import permutations
for i in permutations(list(range(150))):
# do something with i
However this will create many sequences that I don't need to check. I can also restrict permutation length with r such that
from itertools import permutations
for i in permutations(list(range(150)), r=80):
# do something with i
However for really bad sequences there will still be a lot of redundant checks. Additionally this could stop before a 'best' solution.
I could do something like
from itertools import permutations
v = []
for i in permutations(list(range(150)), r=80):
if v and v == i[:len(v)]:
continue
# do something with i
v = i # would be some optimal subset of i
However this still takes a very long time to run as Python is still generating and checking the sequences. Any thoughts on how I should approach this? Ideally I would be able to run the checks in parallel.
More Background: I am attempting to create optimized resource graphs for a game called Black Desert Online( graph example at somethinglovely.net/bdo/ ). The graph has ~150 resource nodes that can each connect to a subset of 14 root nodes. Intermediate nodes on the graph have weights associated with them. Each city has a maximum amount of nodes it can be connected to and there is an additional weight cost for connecting a resource node to a city. I was not having success with random graph generation coupled with a genetic algorithm for 'finding' an optimal solution. Additionally just making greedy choices leads to a sub-optimal solution. I am currently stumped on how to generate a brute force + comprehensive solution that will run in a reasonable period of time(reasonable being within a day on a reasonable desktop computer.
Upvotes: 0
Views: 293
Reputation: 77837
Go through the inventory list, one item at a time, and try packing both with and without that item (two recursions). Report a solution when we reach one of two points:
This takes care of the culling, by proactive construction.
Code:
items = [
# Description, weight
("petrol", 10),
("clothes", 8),
("tents", 7),
("beer", 16),
("food", 20),
("teddy bear", 3),
("tank", 25),
("skin lotion", 2),
("library", 17),
("mortar", 9),
("cut lumber", 12),
("sports gear", 14),
]
limit = 20
def load(inventory, max_weight, current):
still_okay = [item for item in inventory if item[1] <= max_weight]
if len(still_okay) == 0:
# Can't add any more; emit solution and back up
print "RESULT", current
else:
# If the rest of the list fits in our weight budget,
# take everything.
if sum([item[1] for item in still_okay]) <= max_weight:
print "RESULT", current + still_okay
else:
item = still_okay.pop()
# recur on two branches: one each with and without this item
load(still_okay, max_weight - item[1], current + [item])
load(still_okay, max_weight, current)
load(items, limit, [])
Output:
RESULT [('sports gear', 14), ('teddy bear', 3), ('skin lotion', 2)]
RESULT [('cut lumber', 12), ('skin lotion', 2), ('teddy bear', 3)]
RESULT [('cut lumber', 12), ('teddy bear', 3)]
RESULT [('cut lumber', 12), ('tents', 7)]
RESULT [('cut lumber', 12), ('clothes', 8)]
RESULT [('mortar', 9), ('skin lotion', 2), ('teddy bear', 3)]
RESULT [('mortar', 9), ('skin lotion', 2), ('tents', 7)]
RESULT [('mortar', 9), ('skin lotion', 2), ('clothes', 8)]
RESULT [('mortar', 9), ('teddy bear', 3), ('tents', 7)]
RESULT [('mortar', 9), ('teddy bear', 3), ('clothes', 8)]
RESULT [('mortar', 9), ('tents', 7)]
RESULT [('mortar', 9), ('clothes', 8)]
RESULT [('mortar', 9), ('petrol', 10)]
RESULT [('library', 17), ('skin lotion', 2)]
RESULT [('library', 17), ('teddy bear', 3)]
RESULT [('skin lotion', 2), ('teddy bear', 3), ('tents', 7), ('clothes', 8)]
RESULT [('skin lotion', 2), ('teddy bear', 3), ('clothes', 8)]
RESULT [('skin lotion', 2), ('teddy bear', 3), ('petrol', 10)]
RESULT [('skin lotion', 2), ('beer', 16)]
RESULT [('skin lotion', 2), ('tents', 7), ('clothes', 8)]
RESULT [('skin lotion', 2), ('tents', 7), ('petrol', 10)]
RESULT [('skin lotion', 2), ('petrol', 10), ('clothes', 8)]
RESULT [('teddy bear', 3), ('beer', 16)]
RESULT [('teddy bear', 3), ('tents', 7), ('clothes', 8)]
RESULT [('teddy bear', 3), ('tents', 7), ('petrol', 10)]
RESULT [('teddy bear', 3), ('clothes', 8)]
RESULT [('teddy bear', 3), ('petrol', 10)]
RESULT [('food', 20)]
RESULT [('beer', 16)]
RESULT [('tents', 7), ('clothes', 8)]
RESULT [('tents', 7), ('petrol', 10)]
RESULT [('petrol', 10), ('clothes', 8)]
Upvotes: 1