I've a set of student and task which I wish to assign. How can I add an objective function so that I get a balance number of task assigned to each student?

Subsequently, I will also add some constraints to restrict certain student(s) from receiving certain task hence I would like to balance out the task per student as much as I could.

For example the codes below, I have 5 student and 13 tasks, some student would have received 2 task or 3 task which is the most optimal. The objective function I could think of is using the max number task - min number of task but I am unable to form the logic using the or-tool syntax.

Thanks in advance!

students = [0,1,2,3,4]
tasks = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

model = cp_model.CpModel()
max_task = math.ceil(len(tasks)/len(students))

x = {}
for student in students:
    for task in tasks:
        x[(student ,task)] = model.NewBoolVar(f'student_{student}_task_{task}')

# add constraint: each task must be assigned to exactly one student 
for task in tasks:
    model.Add(sum(x[student ,task] for student in students) == 1)

for student in students:
    model.Add(sum(x[(student,task)] for task in tasks) > 0)
    model.Add(sum(x[(student,task)] for task in tasks) <= max_task)

# add a objective function here

solver = cp_model.CpSolver()
status = solver.Solve(model)
print(status)

if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
    for worker, task in x:
        if solver.Value(x[(student,task)])==1:
            print(f'Student {student} is assigned to Task {task}')
else:
    print("No solution found.")
1

There are 1 answers

0
Laurent Perron On BEST ANSWER

You can have a look at the balance_group example.

It uses this trick to minimize the spread

    e = model.NewIntVar(0, 550, "epsilon")

    # Constrain the sum of values in one group around the average sum per group.
    for g in all_groups:
        model.Add(
            sum(item_in_group[(i, g)] * values[i] for i in all_items)
            <= average_sum_per_group + e
        )
        model.Add(
            sum(item_in_group[(i, g)] * values[i] for i in all_items)
            >= average_sum_per_group - e
        )