1
$\begingroup$

I teach a large class, with $N$ students, where groups of 4 to 5 students are assigned to a unique set of a projects. Each student provides a list of their top $m$ ranked preferences of the project they'd like to work on. There are generally more projects available than there are students to fill them, i.e. I drop some projects. Additionally, I collect some survey data with a number of questions that allow me to form a continuous objective function for any given organization of teams, i.e. given a unique set of team members I can output a continuous value based on their survey responses that should be maximized for each team. My goal is to algorithmically choose optimal teams such that the objective is maximized and students are placed with a project that is as high as possible on their ranking list.

I assume that my unknown is a set of integers that assign the students into $M$ teams. I'm not quite sure that I can incorporate the objective of project preference into the continuous objective function.

I have yet to home in on analogous mathematical optimization problems or the correct language to search for that give clues how this might be best solved. I'm asking for some leads on how this may be solved.

  • 0
    You could model this as an [assignment problem](https://en.wikipedia.org/wiki/Assignment_problem). See the Example section in the Wikipedia article for how it can handle the fact that up to 5 students can do a project and that some projects can be dropped.2017-01-16

1 Answers 1

1

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:                                                                                
  • 0
    This looks great. I'm unfamiliar with the software and the language, so it will take a bit to review. I'll install and try it out and report back. Thanks!2017-01-19
  • 0
    Another [team scheduling example](http://math.stackexchange.com/a/1830575/58610) which might inspire you.2017-01-19
  • 0
    I finally had time to dig into this. I'm new to discrete optimization so this is very interesting stuff.2017-06-27
  • 0
    Pressed submit too early above. This probably solves my problem as stated, but I didn't describe the fitness functions that well. The fitness function I'd like to use is not simply a function of a single student and their assigned team, but is a function of all students in a team. For example, I might have a fitness values associated with whether a given team has teammates with schedules that are aligned. Does minizinc allow for complicated definitions of the maximize function? I wouldn't want to have to define the fitness value for every single possible combination of groupings.2017-06-27
  • 0
    MiniZinc allows arbitrary complex fitness functions. In practice, you should try to keep it simple and stick to discrete integer functions.2017-06-28