My attempt to solve the optimization problem with the MiniZinc constraint solver:
%
% Optimum assignment of students to teams and projects
%
int: N = 20; % number of students
int: P = 10; % number of projects
int: M = 3; % number of project preferences per student;
set of int: Preferences = 1 .. M;
set of int: Projects = 1 .. P;
set of int: Students = 1 .. N;
set of int: Fitness = 1 .. 10;
% Each of the students has M preferences
% Hypothetical sample data (to be changed for every class)
array[Preferences, Students] of Projects: choices =
% 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
[| 1, 2, 3, 4, 5, 5, 4, 3, 2, 7, 8, 9, 9, 2, 1, 3, 3, 1, 1, 1 |
2, 4, 6, 8,10, 2, 6, 4, 8,10, 6, 4, 2, 1, 5, 7, 9,10,10, 8 |
3, 1, 5, 6, 9, 1, 3, 2, 7, 9, 4, 2, 1, 3, 4, 9,10, 2, 3, 6 |];
% choiceGrace points depending on the fulfillment of a preference
array[Preferences] of Fitness: choiceGracePoints = [10, 6, 2];
% Level of fitness for every student and project
% The higher the better ....
% Hypothetical sample data (to be changed for every class)
array[Projects, Students] of Fitness: survey =
% 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
[| 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 |
5, 1, 7, 2, 5, 5, 5, 5, 5, 6, 5, 5, 7, 2, 1, 6, 5, 5, 6, 5 |
3, 5, 5, 5, 5, 3, 5, 5, 5, 3, 5, 5, 5, 3, 5, 5, 5, 5, 8, 5 |
5, 5, 5, 1, 5, 5, 4, 5, 1, 5, 5, 4, 5, 5, 4, 5, 3, 2, 5, 5 |
5, 5, 7, 4, 5, 2, 5, 7, 5, 8, 5, 5, 5, 2, 5, 5, 5, 8, 1, 5 |
2, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,10, 5 |
5, 3, 5, 3, 5, 1, 5, 4, 3, 5, 5, 5, 3, 5, 1, 5, 3, 5, 5, 5 |
4, 2, 5, 5, 8, 5, 5, 5, 5, 8, 5, 2, 5, 4, 7, 5, 7, 2, 5, 5 |
9, 3, 5, 4, 5, 3, 5, 6, 5, 5, 5, 6, 5, 3, 5, 5, 1, 3, 6, 5 |
10, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,10, 5, 5, 5, 5,10, 5, 5 |];
array[Students] of string: names =
["S01", "S02", "S03", "S04", "S05", "S06", "S07", "S08", "S09", "S10",
"S11", "S12", "S13", "S14", "S15", "S16", "S17", "S18", "S19", "S20" ];
% each student is assigned to a project
array[Students] of var Projects: assignments;
var int: choiceGrace;
var int: choiceFitness;
% constraints
% number of students per team/project must be 0, 4 or 5
constraint
forall (p in Projects) (
let { var int: occ = sum([assignments[s] == p | s in Students]) }
in (occ == 4) \/ (occ == 5) \/ (occ == 0)
);
% each student must be assigned to one of his/her selected projects
constraint
forall (s in Students) (
exists(p in Preferences)(assignments[s] == choices[p, s])
);
% objective variables (weights could be adjusted)
constraint
choiceGrace == sum([if assignments[s] == choices[p, s] then choiceGracePoints[p] else 0 endif
| s in Students, p in Preferences]);
constraint
choiceFitness = sum([survey[assignments[s], s] | s in Students]);
% selection fitness and survey fitness are added with equal weights
solve maximize choiceGrace + choiceFitness;
%
% Output solution
%
output
[ "choiceFitness = " ++ show(choiceFitness) ++ "\n" ] ++
[ "choiceGrace = " ++ show(choiceGrace) ] ++
[ if s == 1 then "\n" ++ show(p) ++ ": " else "" endif ++
show(if assignments[s] == p then names[s] else " " endif) ++ " "
| p in Projects , s in Students ];
Sample solution output:
choiceFitness = 93
choiceGrace = 164
1: S15 S18 S19 S20
2: S01 S06 S09 S14
3: S03 S08 S16 S17
4: S02 S04 S07 S11
5:
6:
7:
8:
9: S05 S10 S12 S13
10: