I'm designing an algorithm that creates optimum teams based on everyone's availabilities (maximizes the amount of shared availability). To this end, I've made a function that takes in a dictionary of availabilities mapping person to an array of their availabilities throughout the week at every 15 minute point (e.g. [True, True, False,...,False]) and desired team size and outputs final teams. Here's my working code:
import numpy as np
from itertools import combinations, chain
import operator
from functools import reduce
from tqdm import tqdm
import operator
def ncr(n, r):
"""N Choose R combination"""
r = min(r, n-r)
numer = reduce(operator.mul, range(n, n-r, -1), 1)
denom = reduce(operator.mul, range(1, r+1), 1)
return numer // denom # or / in Python 2
NUM_PEOPLE = 100
TEAM_SIZE = 5
time_slots = 24*4*7
# random availabilities
availabilities = np.random.randint(2, size=(NUM_PEOPLE, time_slots), dtype="bool")
availabilities_dict = {}
for i, availability in enumerate(availabilities):
availabilities_dict[i] = availability
def make_teams(availabilities_dict, team_size):
"""
availabilities_dict form: {player: [0,0,0,...,1], player2: [0,0,0,0,1...,0]}
"""
sums = []
comb = combinations(availabilities_dict, TEAM_SIZE)
for i in tqdm(list(comb)):
sum_of_array = np.logical_and.reduce((np.array(availabilities[i,:]))).sum()
sums.append((i, sum_of_array))
sums.sort(key=operator.itemgetter(1))
sums.reverse()
assigned = []
teams = []
i = 0
while (len(assigned) < NUM_PEOPLE):
considering = list(sums[i][0])
none_assigned = True
for person in considering:
if person in assigned:
none_assigned = False
if none_assigned:
teams.append((considering, sums[i][1]))
for person in considering:
assigned.append(person)
i += 1
return teams
teams = make_teams(availabilities_dict, TEAM_SIZE)
Example output consists of a list of tuples (team, total blocks intersecting):
[([15, 18, 42, 70, 94], 36),
([14, 30, 63, 80, 97], 36),
([1, 12, 40, 64, 88], 36),
([22, 48, 62, 72, 87], 34),
([25, 29, 35, 53, 85], 32),
([26, 31, 49, 60, 78], 31),
([13, 44, 79, 93, 96], 29),
([32, 59, 67, 71, 82], 28),
([24, 39, 41, 50, 91], 28),
([9, 28, 55, 92, 95], 28),
([5, 8, 11, 19, 86], 28),
([4, 33, 56, 76, 83], 27),
([2, 10, 16, 17, 69], 27),
([21, 43, 73, 75, 81], 24),
([7, 20, 57, 89, 99], 24),
([23, 36, 37, 58, 66], 22),
([52, 61, 65, 68, 84], 20),
([3, 27, 34, 47, 74], 18),
([6, 38, 51, 54, 77], 16),
([0, 45, 46, 90, 98], 9)]
But this took over 10 mins to run on my state of the art computer! Is there a way to still find an optimum team match without running through all of the possible combinations? How can I make it faster? Thank you!