I currently have a fairly simple algorithm that tries to build the best possible team-lineup given some constrains:
- There is a finite list of events which needs to be filled with swimmers
- Event 1: 50 Free
- Event 2: 100 Free
- ...
- In each event, a team can send up to N (
MaxSwimmersPerTeam) swimmers. - A single swimmer can participate in multiple events, but is limited by M (
MaxEntriesPerSwimmer). - I have a list of swimmer best times for every event they have participated. (Note: Not every swimmer swims every possible event, it's a subset with size close to M).
team_1_best_times = [
{"time_id": 1, "swimmer_id": 1, "event_id": 1, "time": 22.00, "rating": 900.00},
{"time_id": 2, "swimmer_id": 1, "event_id": 2, "time": 44.00, "rating": 800.00},
{"time_id": 3, "swimmer_id": 2, "event_id": 1, "time": 22.10, "rating": 890.00},
{"time_id": 4, "swimmer_id": 2, "event_id": 2, "time": 46.00, "rating": 750.00},
]
The rating key (Higher is Better) gives me the ability to compare times across different events.
The best-possible line-up would be the lineup with max average rating across all chosen times satisfying N and M
My current approach is to iterate over all the times sorted by rating and fill the line-up until N and M are satisfied.
For example given N=1 and M=1 my algorithm would:
- Put
Time1(of Swimmer1) with 900 rating intoEvent1 - Skip
Time3(Event1-Swimmer2) with890rating - since we already have1= N other swimmer (Swimmer1) inEvent1, thusMaxSwimmersPerTeamhas been reached. - Skip
Time2(Event2-Swimmer1) with800rating - sinceSwimmer1has already been put in1= M other Event (Event1), thus (MaxEntriesPerSwimmer) has been reached. - Put
Time4(Swimmer2) with750rating intoEvent2.
Now the average rating of this team-lineup Event1-Swimmer1 (900) and Event2-Swimmer2 (750) would be: (900+750)/2 = 825.
And this is the simplest possible example showing where my approach falls short. What a smart coach could do, would be to put Swimmer1 into Event2 (800) and Swimmer2 into Event1 (890) reaching a higher avg rating of: (890+800)/2 = 845.
I've tried to research the problem a little bit, found libraries like python-constraint , gekko, pyomo but I still cannot figure out how to explain the problem using their tools.
Swimmer allocation is related to schedule optimization or set covering algorithms. Heuristic methods can often get close to the optimal solution with a simple sort & select method. Optimization methods are typically slower, but can find a more optimal solution. Below is a simple Mixed Integer Linear Program (MINLP) that solves a simplified version of your swimmer allocation problem in
gekko.The solution is:
I recommend a first look at heuristic methods as a more flexible / configurable solution. Heuristic methods need lower computing resources to get a quick solution versus a more optimal solution. If you do want to use MILP, a heuristic method is a way to initialize the solver with a good initial guess.