Commit 9d09fc53 authored by Jirka Fink's avatar Jirka Fink
Browse files

Add 3rd assignment

parent 47ef5212
import heapq
import signal
import random
def find_balanced_partition(bin_cnt, prices, time_limit):
"""
Find partition of items of given prices into a given number of bins.
bin_cnt - Number of bins
prices - List of prices of items
time_limit - Time limit for calculation in seconds
returns - List of bins, each one is a list of prices of items
TODO: Find a better solution than the trivial one.
"""
bins = [ [] for _ in range(bin_cnt) ]
bins[0] = prices
return bins
import sys
import random
import copy
from time import time
from prettytable import PrettyTable
from partition import find_balanced_partition
# Returns True if the solution is feasible for the given instance; otherwise, an error string.
# If the optimal number of bins is provided, the optimality of the solution is tested.
def verify_bins(bin_cnt, prices, bins):
"""
Test whether a given solution is feasible
bin_cnt - Number of bins
prices - List of prices of items
return - A pair:
1. Value of the objective function if the solution is feasible; and -1 otherwise
2. An error string
"""
if len(bins) != bin_cnt:
return (-1, "The number of bins should be {} but your solution contains {} bins".format(bin_cnt, len(bins)))
occurences_in_instance = dict()
for p in prices:
occurences_in_instance[p] = occurences_in_instance.get(p, 0) + 1
occurences_in_solution = {p : 0 for p in occurences_in_instance.keys()}
for k in bins:
for p in k:
if p not in occurences_in_instance:
return (-1, "There is no item of price {} in the instance".format(p))
occurences_in_solution[p] += 1
for p in occurences_in_instance.keys():
if occurences_in_instance[p] != occurences_in_solution[p]:
return (-1, "Item of price {} occurs {} times in the instance but {} times in your solution".format(occurences_in_instance[p], occurences_in_solution[p]))
costs = [ sum(k) for k in bins ]
return (max(costs) - min(costs), "Feasible solution")
def run_tests(item_cnt, bin_cnt, min_price, max_price, tests, max_avg_gap, seed):
rng = random.Random(seed)
sum_gap = 0
for _ in range(tests):
prices = [ rng.randint(min_price, max_price) for _ in range(item_cnt) ]
bins = find_balanced_partition(bin_cnt, copy.deepcopy(prices), 1)
gap,msg = verify_bins(bin_cnt, prices, bins)
if gap == -1:
return (False, msg)
sum_gap += gap
avg_gap = sum_gap / tests
if avg_gap > max_avg_gap:
return (False, "Your average gap is {} which is too large".format(avg_gap))
return (True, "Correct solutions, average gap is {}".format(avg_gap))
def main():
tests = {
# (item_cnt, bin_cnt, min_price, max_price, tests, max_avg_gap, seed, points)
"trivial": (10, 3, 100, 200, 10, 10, 2157, 0),
"few_small": (2000, 200, 1, 100, 10, 2, 6537, 2),
"many_small": (20000, 2000, 1, 100, 10, 2, 54326, 2),
"few_large": (400, 100, 9000, 10000, 10, 15, 78457, 2),
"many_large": (40000, 10000, 9000, 10000, 10, 10, 4579, 2),
"very_large": (30000, 10000, 900000, 1000000, 10, 10000, 4579, 2),
}
if len(sys.argv) == 1:
results = PrettyTable(["Instance name", "Points", "Items", "bins", "Min price", "Max price", "Max gap", "Time limit [s]", "Your time [s]", "Evaluation"])
for name in tests:
print("Running test", name)
item_cnt, bin_cnt, min_price, max_price, test_cnt, max_avg_gap, seed, points = tests[name]
start_time = time()
status, msg = run_tests(item_cnt, bin_cnt, min_price, max_price, test_cnt, max_avg_gap, seed)
running_time = time() - start_time
print(msg)
print()
results.add_row([name, points, item_cnt, bin_cnt, min_price, max_price, max_avg_gap, 11, running_time, msg])
print(results)
else:
name = sys.argv[1]
if name in tests:
item_cnt, bin_cnt, min_price, max_price, test_cnt, max_avg_gap, seed, points = tests[name]
status, msg = run_tests(item_cnt, bin_cnt, min_price, max_price, test_cnt, max_avg_gap, seed)
print(msg)
else:
print("Unknown test", name)
"""
To run all tests, run the command
$ python3 partition_tests.py
To run a test NAME, run the command
$ python3 partition_tests.py NAME
"""
if __name__ == "__main__":
main()
In the balanced-partition problem, we are given n items, each having an integer price p_i for i = 1,..,n, and k bins of unlimited capacity. The objective is to assign items into bins so that total costs of items in all bins are as evenly distributed as possible; i.e. the difference of costs between the most expensive and the cheapest bins is minimized, called a gap.
The task is to implement the function *find_balanced_partition* in the file *partition.py* which is expected to find a *heuristic* solution to a given instance. The heuristic feasible solution has to be found within a time limit which the function find_balanced_partition obtains by an argument. To ensure that the your algorithm does not run too long, you can use a signal handler; see the file *timer.py* for more details.
Your algorithm is tested on 5 tests and a trivial one for debugging purposes, and each test contains 10 instances. In order to pass a test, your algorithm must find a feasible solution for every instance within a given time limit and the average gap must be smaller than some value.
In you program, explain your algorithm.
If you are interested, you can compare results of your algorithm with the following table (tested on Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, 32BG RAM):
+---------------+--------+-------+-------+-----------+-----------+---------+----------------+--------------------+------------------------------------------+
| Instance name | Points | Items | bins | Min price | Max price | Max gap | Time limit [s] | Your time [s] | Evaluation |
+---------------+--------+-------+-------+-----------+-----------+---------+----------------+--------------------+------------------------------------------+
| trivial | 0 | 10 | 3 | 100 | 200 | 10 | 11 | 10.001003742218018 | Correct solutions, average_gap is 7.6 |
| few_small | 2 | 2000 | 200 | 1 | 100 | 2 | 11 | 2.041139841079712 | Correct solutions, average_gap is 1.2 |
| many_small | 2 | 20000 | 2000 | 1 | 100 | 2 | 11 | 3.397735834121704 | Correct solutions, average_gap is 1.3 |
| few_large | 2 | 400 | 100 | 9000 | 10000 | 15 | 11 | 10.00636100769043 | Correct solutions, average_gap is 10.6 |
| many_large | 2 | 40000 | 10000 | 9000 | 10000 | 10 | 11 | 10.540471076965332 | Correct solutions, average_gap is 7.4 |
| very_large | 2 | 30000 | 10000 | 900000 | 1000000 | 10000 | 11 | 10.499473571777344 | Correct solutions, average_gap is 7422.5 |
+---------------+--------+-------+-------+-----------+-----------+---------+----------------+--------------------+------------------------------------------+
import signal, os
def timer_test(limit):
def handler(signum, frame):
print('Signal handler called with signal', signum)
nonlocal terminate
terminate = True
# Set the signal handler and an alarm
terminate = False
signal.signal(signal.SIGALRM, handler)
signal.alarm(limit)
# Place your initialization here
while not terminate:
# Place your optimization code here
pass
# Disable the alarm
signal.alarm(0)
timer_test(3)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment