You could also use a MIP. Here's an example using JuMP and HiGHS:
julia> using JuMP, HiGHS
julia> function solve_problem(;
meetings::Vector{Set{Int}},
num_time_slots::Int,
num_people::Int,
)
model = Model(HiGHS.Optimizer)
set_silent(model)
# z[meeting, t] is 1 if `meeting` meets in time slot `t`
@variable(model, z[meeting in meetings, t in 1:num_time_slots], Bin)
# Meeting `meeting` can meet in at most one time slot
@constraint(model, [meeting in meetings], sum(z[meeting, :]) <= 1)
# Person `person` can meet with at most one meeting in time slot `t`
@constraint(
model,
[person in 1:num_people, t in 1:num_time_slots],
sum(z[meeting, t] for meeting in meetings if person in meeting) <= 1
)
# Maximize the number of meetings that meet
@objective(model, Max, sum(z))
# Solve the problem and check we found a solution
optimize!(model)
@assert is_solved_and_feasible(model)
num_scheduled = round(Int, objective_value(model))
println("Scheduled $(num_scheduled) / $(length(meetings)) meetings")
solution = Dict(t => [] for t in vcat(nothing, 1:num_time_slots))
for meeting in meetings
t = findfirst(t -> value(z[meeting, t]) >= 0.5, 1:num_time_slots)
push!(solution[t], meeting)
end
return solution
end
solve_problem (generic function with 1 method)
julia> num_people = 50
50
julia> num_time_slots = 7
7
julia> meetings = Set{Int}[]
Set{Int64}[]
julia> while length(meetings) < 130
meeting = Set(rand(1:num_people, rand(2:3)))
if !(meeting in meetings)
push!(meetings, meeting)
end
end
julia> solution = solve_problem(; meetings, num_people, num_time_slots)
Scheduled 115 / 130 meetings
Dict{Union{Nothing, Int64}, Vector{Any}} with 8 entries:
nothing => [Set([29, 26]), Set([47, 16, 48]), Set([21, 43]), Set([21, 33]), Set([47, 45]), Set([35, 18, 48]), Set([33, 9]), Set([15, …
4 => [Set([37, 3]), Set([34, 40]), Set([15, 45]), Set([42, 14, 19]), Set([6, 43, 10]), Set([41, 33]), Set([5, 21, 18]), Set([27…
5 => [Set([43, 24]), Set([34, 11]), Set([33, 27]), Set([45, 25]), Set([22, 6, 42]), Set([50, 46, 14]), Set([10, 18, 9]), Set([4…
6 => [Set([39, 41]), Set([20, 37, 40]), Set([46, 23]), Set([47, 27, 28]), Set([4, 38]), Set([50, 33, 1]), Set([30, 17]), Set([4…
2 => [Set([5, 24]), Set([22, 6]), Set([47, 2]), Set([9, 31, 46]), Set([14, 1]), Set([18, 37]), Set([42, 40]), Set([25, 12]), Se…
7 => [Set([4, 46]), Set([7, 2, 21]), Set([47, 26, 17]), Set([36, 31, 3]), Set([22, 8]), Set([41, 27, 42]), Set([6, 18, 1]), Set…
3 => [Set([41, 33, 36]), Set([32, 6]), Set([49, 35, 30]), Set([3, 23]), Set([21, 31]), Set([24, 42, 48]), Set([13, 47]), Set([2…
1 => [Set([45, 8]), Set([21, 25, 11]), Set([14, 37]), Set([15, 33, 19]), Set([26, 28]), Set([22, 30]), Set([47, 29]), Set([32,
I once built something very similar, https://github.com/odow/interview-scheduler, but it requires https://solverstudio.org to run, and I haven't touched it in 10 years.