Let us call this problem the Slinger-Bird problem (actually the Slinger is analogous to a server and the bird to a request but I was having a nervous breakdown thinking abou
I suggest using bitmaps for the slingers and birds, i.e.
S1 = B1 = 1, S2 = B2 = 2, S3 = B3 = 4, B4 = 8
Then the input data can be written as
bird_data = [[3, 0, 1], [7, 1, 12], [8, 3, 0], [0, 12, 15]]
The cost function can now be written like this:
def cost(shots):
hit = units = 0
for show, shot in zip(bird_data, shots):
units += 1
for n, birds in enumerate(show):
if shot & 1:
units += 1
hit |= birds
if hit == 15: # all are hit
return units
shot >>= 1
return 99 # penalty when not all are hit
Now it's easy to find the optimal shots by calculating the minimum of the cost function:
from itertools import product
shot_sequences = product(*([range(7)]*len(bird_data)))
print min((cost(shots), shots) for shots in shot_sequences)
This prints
(4, (0, 5, 0, 0))
which means the optimum is 4 units, when S1 and S3 (5 = 1 + 4) fire at t=2. Of course your solution is also possible, where S1 fires at t=1 and S3 at t=2, both have the same cost.
However, since the algorithm is using brute force, running over all possible shot sequences, it is only fast and feasible when the data sets are very small, like in your example.
I'm assuming you know all the numbers you give in the example when you're starting the algorithm, and don't get t2 after finishing t1 etc.
I also assume two slingers can fire at once, though that shouldn't matter much.
At the first choice, you can assign a value to each cell, being amountOfBirdsInCell-time.
This gives you two cells with values 1, being S1t1, S1t2, the rest is lower.
Only the time of the last cell counts in your score, so picking the earliest one will remove the time on it for the next round, making it the most valuable time. That's the first pick.
Now, remove the birds killed in that first pick from all cells.
Repeat the value determining process for the remaining cells. In your example, cell S3t2 will give the highest result, being 0.
Repeating this process, you get the most valuable cells at the earliest times.
One important bit your example doesn't cover: If your first most valuable pick is at t2, the next most valuable pick might be at t1 or t2, so you should take those into account. However, since t2 is already confirmed, you should not take their time into account for their value.
I've never written in python, I'm just here because of the algorithm tag, so here's some java/c-like pseudo code:
highestCellTime = 0;
while(birdsRemain)
{
bestCell;
for(every cell that has not been picked yet)
{
currentCellValue = amountOfBirds;
if(currentCellTime > highestCellTime)
{
currentCellValue = currentCellValue - currentCellTime;
}
if(currentCellValue < bestCellValue)
{
bestCell = thisCell;
}
else if(currentCellValue == bestCellValue && currentCellTime < bestCellTime)
{
bestCell = thisCell;
}
}
addCellToPicks(bestCell);
removeBirdsFromOtherCells(bestCellBirds);
}
Unless I forgot something, you now have an optimal combination of cells in your picks collection.
I hope this code makes sense to a python programmer. If someone can translate it, please do! And please remove this bit of text and the earlier mention of java/c-pseudocode when you do.
EDIT by OP: First version and does not end up with the best cells. I am guessing it must be a bug in my code but nevertheless I am posting here.
import math
cellsNotPicked = range(0,12)
cellToBird = {
0: [1, 2],
1: [],
2: [1],
3: [1,2,3],
4: [1],
5: [3,4],
6: [4],
7: [1,2],
8: [],
9: [],
10: [3,4],
11: [1,2,3,4]
}
picks = []
def getCellValue(cell):
return len(cellToBird[cell])
def getCellTime(cell):
return int(math.floor(cell / 3)) + 1
birdsRemain = 4
while(birdsRemain > 0):
bestCell = 0;
for thisCell in cellsNotPicked:
currentCellValue = getCellValue(thisCell);
currentCellTime = getCellTime(thisCell)
highestCellTime = getCellTime(bestCell)
if(currentCellTime > highestCellTime):
currentCellValue = currentCellValue - currentCellTime;
if(currentCellValue < getCellValue(bestCell)):
bestCell = thisCell
elif (currentCellValue == getCellValue(bestCell)) and (currentCellTime < getCellTime(bestCell)):
bestCell = thisCell
picks.append(bestCell)
cellsNotPicked.remove(bestCell)
birdsToRemove = cellToBird[bestCell]
for key in cellToBird:
for bird in birdsToRemove:
try:
cellToBird[key].remove(bird)
birdsRemain -= 1
except:
pass
print picks
This is not an optimal assignment problem, because slingers kill all birds in view.
You have a two-dimensional objective function, so there can be a number of tradeoffs between shots and time. Determining the minimum number of shots for a particular time limit is exactly the set cover problem (as mhum suggests). The set cover problem is NP-hard and hard to approximate, but in practice, branch and bound using the dual of the linear programming formulation is quite effective in finding the optimum.