public static function compute_groups($input_data)
 {
     // Step 1: Set up empty groups & alternative format of input.
     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     $Num_Stu = count($input_data);
     //$Num_Groups = ($Num_Stu - $Num_Stu % 5) / 5;  // The documented algorithm.
     $Num_Groups = max(1, ($Num_Stu - $Num_Stu % 4) / 4);
     // This works better.
     // Explanation: Setting $Num_Groups = $Num_Stu div n always creates
     //   enough groups so that each group can have at least n members.
     //   (Because, $Num_Stu div n <= $Num_Stu / n, so $Num_Stu / $Num_Groups >= n).
     //   If there are less than n students in the class, then, even though
     //   formally there are not enough, we want to have one group anyway.
     // List of groups with list of associated states.
     $Groups = [];
     $NeededRoles = [];
     // Parallel array; roles yet to be covered.
     for ($i = 1; $i <= $Num_Groups; $i++) {
         $Groups[$i] = [];
         $NeededRoles[$i] = array(1 => TRUE, 2 => TRUE, 3 => TRUE, 4 => TRUE);
         // Bitfield for options 1, 2, 3, 4.
     }
     // StuCapabilities is an unchanging copy of the input, slightly different format.
     // RolePools is a list of decreasing pools from which student ids are drawn.
     //   One "pool" per role.
     $StuCapabilities = array();
     foreach (array_keys($input_data) as $S_i) {
         $StuCapabilities[$S_i] = [];
     }
     $RolePools = array(1 => [], 2 => [], 3 => [], 4 => []);
     // Fill StuCapabilities and RolePools.
     foreach ($input_data as $S_i => $RoleRespList_i) {
         foreach ($RoleRespList_i as list($RoleNum_j, $RoleResp_j)) {
             if ($RoleResp_j) {
                 $RolePools[$RoleNum_j][] = $S_i;
                 $StuCapabilities[$S_i][] = $RoleNum_j;
             }
         }
     }
     // Step 2: Distribute students with rare roles.
     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // Arbitrarily pick one of the smallest in RolePools. (Cred: Amal Murali @ StackOverflow)
     $PoolSizes = array_map('count', $RolePools);
     $MinPoolSize = min($PoolSizes);
     $MinIndex = array_flip($PoolSizes)[$MinPoolSize];
     $RarePool =& $RolePools[$MinIndex];
     // Alias, so use reference op.
     // Distribute the students who picked the chosen rare role.
     $NumToDistribute = min($MinPoolSize, $Num_Groups);
     for ($count = 1; $count <= $NumToDistribute; $count++) {
         $S = SugAlg::PickOne($RarePool);
         SugAlg::AddStudent($S, $Groups[$count], $StuCapabilities, $NeededRoles[$count], $RolePools);
     }
     // Step 3: Try to fill all roles for all groups.
     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // While there are groups that still needs roles
     //   (roles which have not already been exhausted)...
     //REVISION-STEP: Try to use array_filter.
     while ($RemGrpIds = array_keys(array_map('SugAlg::HasNeededRoles', $NeededRoles), TRUE, TRUE)) {
         // For each of these groups...
         foreach ($RemGrpIds as $x) {
             $Groups_x =& $Groups[$x];
             // Alias, so use reference op.
             $NeededRoles_x =& $NeededRoles[$x];
             // Alias, so use reference op.
             // Pick a needed role (might be exhausted).
             $R = array_search(TRUE, $NeededRoles_x);
             // If there's still students for that role (not exhausted)...
             if (!empty($RolePools[$R])) {
                 $S = SugAlg::PickOne($RolePools[$R]);
                 SugAlg::AddStudent($S, $Groups_x, $StuCapabilities, $NeededRoles_x, $RolePools);
             } else {
                 $NeededRoles_x[$R] = FALSE;
             }
         }
     }
     return $Groups;
 }
Example #2
0
// ----------------------------------------------------------------------------
// Prepare input by mapping question numbers to role numbers.
// ----------------------------------------------------------------------------
// Bijective map.
$map_ques_to_role = array(15 => 1, 16 => 2, 17 => 3, 18 => 4);
// Modify $student_list, applying the map to question numbers.
foreach ($student_list as &$role_response_list) {
    foreach ($role_response_list as &$ques_resp_pair) {
        $question_num = $ques_resp_pair[0];
        $ques_resp_pair[0] = $map_ques_to_role[$question_num];
    }
}
// ----------------------------------------------------------------------------
// Invoke suggestion algorithm.
// ----------------------------------------------------------------------------
$groups = SugAlg::compute_groups($student_list);
// ----------------------------------------------------------------------------
// Add names.
// ----------------------------------------------------------------------------
$query = "SELECT student_id, name FROM student_info";
$name_responses = DB::query($query);
$names = array();
foreach ($name_responses as $name_record) {
    $student_id = (int) $name_record['student_id'];
    $name = $name_record['name'];
    $names[$student_id] = $name;
}
// StuId -> [StuId, Name]
function add_name($stu_id)
{
    global $names;