Combinatorial optimization in multiple dimensions
For simplicity, we will assume that every item occurs only once.
Our job is to maximize Subject to the following constraints
is a combination of zeros and ones .
#!/usr/bin/env python3
import numpy as np
def backpack_binary(max_capacity, values, volumes):
""" Solve backpack problem using binary coding approach."""
n = len(values)
max_value = 0
best_combination = np.zeros(n, dtype=int)
# Try all possible combinations using binary representation
# From 0 to 2^n - 1 (all possible binary numbers with n bits)
for i in range(2**n):
selected_items = np.zeros(n, dtype=int)
code = bin(i)[2:].zfill(n) # string representing bits
for i,s in enumerate(code):
selected_items[i] = 1 if s=="1" else 0
total_volume = np.sum(volumes * selected_items)
total_value = np.sum(values * selected_items)
if total_volume <= max_capacity:
if total_value > max_value:
max_value = total_value
best_combination = selected_items.copy()
return max_value, best_combination
def print_solution(values, volumes, max_capacity, max_value, selected_items):
print(f"Backpack Capacity: {max_capacity}")
print(f"Maximum Value: {max_value}")
print(f"Selected Items: {selected_items}")
print(f"{values=}")
print(f"{volumes=}")
print(f"Total number of objects: {len(values)}")
if __name__ == "__main__":
print("====== Example 1 ===========")
values1 = np.array([12, 10, 20, 15], dtype=int)
volumes1 = np.array([2, 1, 3, 2], dtype=int)
max_capacity1 = 5
max_val1, selected1 = backpack_binary(max_capacity1, values1, volumes1)
print_solution(values1, volumes1, max_capacity1, max_val1, selected1)
print("====== Example 2 ===========")
N=20
values2 = (100*np.random.uniform(size=(N))).astype(int)
volumes2 = (80*np.random.uniform(size=(N))).astype(int)
max_capacity2 = 50
import time
t0 = time.time()
max_val2, selected2 = backpack_binary(max_capacity2, values2, volumes2)
print_solution(values2, volumes2, max_capacity2, max_val2, selected2)
print(f"Execution time: {time.time() - t0}")For realization of this algorithm, have a look at the backpack_binaray.py
As with every optimization there are two challenges
Construction of the cost function
Notice the minus!
is a penalty which should grow if we try to put more than fits
Penalty should have the following property
Update of the search point
We will toggle a random
,
it is handy to select position with
i=np.random.randint(0,high=N)
We use prompt
Read 'backpack_binaray.py' and modify it to find the best combo with simulated annealing
algorithm. Use minimization paradigm, i.e. negate maximum value.
Use parameters Tstart, coolingRate, and Niterations.
If backpack items exceed max_capacity, add penalty to value proportional to oversize.
Save resulted script in 'backpack_annealing.py'
the outcome is in backpack_annealing.py
To compare two method, we use the follow up prompt (in the same context)
Write script which compares outcomes of binary and annealing versions,
encode threshold for not running binary search and put it to 15.
Also add relative value to the table.
the outcome script is compare_backpack_solutions.py
It produces the following table (after setting the threshold to 20)
| Problem Size | Binary Value | Annealing Value | Relative Difference | Binary Time | Annealing Time | Speedup |
|---|---|---|---|---|---|---|
| 5 | 132 | 132 | 0.00% | 0.000658s | 0.277721s | 0.00x |
| 8 | 179 | 179 | 0.00% | 0.005239s | 0.270609s | 0.02x |
| 10 | 166 | 122 | 26.51% | 0.020448s | 0.272427s | 0.08x |
| 12 | 251 | 229 | 8.76% | 0.083233s | 0.272598s | 0.31x |
| 15 | 209 | 200 | 4.31% | 0.682702s | 0.271728s | 2.51x |
| 18 | 271 | 269 | 0.74% | 5.521649s | 0.266911s | 20.69x |
| 20 | 396 | 308 | 22.22% | 22.633029s | 0.281073s | 80.52x |
This problem has a lot of connections to the real world. Every time you ask your GPS to find a route, the GPS unit has to solve a similar problem. The traces placement on a printed circuit board is essentially the same problem, as well.
Possible brute force solution
This grows very fast with the increase.
See “The Art of Computer Programming, Volume 4 : Generating All Tuples and Permutations” by Donald Knuth for the discussion of the algorithm. The method below goes back to 14th century India.
Luckily python already has it implemented
import itertools
items = [1, 2, 3]
perms = list(itertools.permutations(items))
In [59]: perms
Out[59]: [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
We use prompt
Write a traveling salesman path searching as `def traveling_salesman_permutations(coord)`,
for a person who always start in 1st city
and end in the last city. Cities coordinate will be encoded in Nx2 array where 1st column is
x and 2nd is y. Solution should return array which lists cities in order of visits.
Implement it via permutation using `intertools.permutations`. When ever possible use numpy
array operations.
Measure and output the path searching time.
Add visualization of the best path mark with different color the 1st and the last city.
And prepare example with 10 cities.
Save outcome to file 'salesman.py'
the outcome is in salesman.py
10 cities path optimization takes about 1.2 seconds on my old home computer.
Prompting
use 'salesman.py' as a base and implement the path search with simulated annealing algorithm.
Save resulting script to 'salesman_annealing.py'
the outcome script is salesman_annealing.py