MDL-25526 Workshop: fixed random allocation of reviews

The patch wraps that foreach ($circles as $circleid => $circle)
loop by yet another one for() loop. Reviews are allocated iteratively
now. During the first iteration, we try to make sure that at least one
circle link exists. During the second iteration, we try to allocate two,
etc. Circles are shuffled at the beginning of each iteration.
This is supposed to improve the randomness of the allocation.

The patch also fixes shuffle_assoc() implementation. The previous
implementation actually did not work at all. Also, that removed called
to shuffle_assoc() was redundant here.
This commit is contained in:
David Mudrak 2011-01-13 14:20:59 +01:00
parent 5a7f931ee5
commit 47ce9385e8

View File

@ -428,76 +428,82 @@ class workshop_random_allocator implements workshop_allocator {
// all users will be processed at once // all users will be processed at once
$circlegroups = array(0); $circlegroups = array(0);
} }
$this->shuffle_assoc($circlegroups);
// $o[] = 'debug::circle groups = ' . json_encode($circlegroups); // $o[] = 'debug::circle groups = ' . json_encode($circlegroups);
foreach ($circlegroups as $circlegroupid) { foreach ($circlegroups as $circlegroupid) {
$o[] = 'debug::processing circle group id ' . $circlegroupid; $o[] = 'debug::processing circle group id ' . $circlegroupid;
$circles = $allcircles[$circlegroupid]; $circles = $allcircles[$circlegroupid];
$this->shuffle_assoc($circles); // iterate over all circles in the group until the requested number of links per circle exists
foreach ($circles as $circleid => $circle) { // or it is not possible to fulfill that requirment
$o[] = 'debug::processing circle id ' . $circleid; // during the first iteration, we try to make sure that at least one circlelink exists. during the
if (!isset($circlelinks[$circleid])) { // second iteration, we try to allocate two, etc.
$circlelinks[$circleid] = array(); for ($requiredreviews = 1; $requiredreviews <= $numofreviews; $requiredreviews++) {
} $this->shuffle_assoc($circles);
$keeptrying = true; // is there a chance to find a square for this circle? $o[] = 'debug::iteration ' . $requiredreviews;
$failedgroups = array(); // array of groupids where the square should be chosen from (because foreach ($circles as $circleid => $circle) {
// of their group workload) but it was not possible (for example there $o[] = 'debug::processing circle id ' . $circleid;
// was the only square and it had been already connected if (!isset($circlelinks[$circleid])) {
while ($keeptrying && (count($circlelinks[$circleid]) < $numofreviews)) { $circlelinks[$circleid] = array();
// firstly, choose a group to pick the square from }
if (NOGROUPS == $gmode) { $keeptrying = true; // is there a chance to find a square for this circle?
if (in_array(0, $failedgroups)) { $failedgroups = array(); // array of groupids where the square should be chosen from (because
// of their group workload) but it was not possible (for example there
// was the only square and it had been already connected
while ($keeptrying && (count($circlelinks[$circleid]) < $requiredreviews)) {
// firstly, choose a group to pick the square from
if (NOGROUPS == $gmode) {
if (in_array(0, $failedgroups)) {
$keeptrying = false;
$o[] = 'error::indent::No more peers available'; // todo translate
break;
}
$targetgroup = 0;
} elseif (SEPARATEGROUPS == $gmode) {
if (in_array($circlegroupid, $failedgroups)) {
$keeptrying = false;
$o[] = 'error::indent::No more peers available in this separate group'; // todo translate
break;
}
$targetgroup = $circlegroupid;
} elseif (VISIBLEGROUPS == $gmode) {
$trygroups = array_diff_key($squaregroupsworkload, array(0 => null)); // all but [0]
$trygroups = array_diff_key($trygroups, array_flip($failedgroups)); // without previous failures
$targetgroup = $this->get_element_with_lowest_workload($trygroups);
}
if ($targetgroup === false) {
$keeptrying = false; $keeptrying = false;
$o[] = 'error::indent::No more peers available'; // todo translate $o[] = 'error::indent::Not enough peers available'; // todo translate
break; break;
} }
$targetgroup = 0; $o[] = 'debug::indent::next square should be from group id ' . $targetgroup;
} elseif (SEPARATEGROUPS == $gmode) { // now, choose a square from the target group
if (in_array($circlegroupid, $failedgroups)) { $trysquares = array_intersect_key($squareworkload, $allsquares[$targetgroup]);
$keeptrying = false; // $o[] = 'debug::indent::individual workloads in this group are ' . json_encode($trysquares);
$o[] = 'error::indent::No more peers available in this separate group'; // todo translate unset($trysquares[$circleid]); // can't allocate to self
break; $trysquares = array_diff_key($trysquares, array_flip($circlelinks[$circleid])); // can't re-allocate the same
$targetsquare = $this->get_element_with_lowest_workload($trysquares);
if (false === $targetsquare) {
$o[] = 'debug::indent::unable to find an available square. trying another group';
$failedgroups[] = $targetgroup;
continue;
} }
$targetgroup = $circlegroupid; $o[] = 'debug::indent::target square = ' . $targetsquare;
} elseif (VISIBLEGROUPS == $gmode) { // ok - we have found the square
$trygroups = array_diff_key($squaregroupsworkload, array(0 => null)); // all but [0] $circlelinks[$circleid][] = $targetsquare;
$trygroups = array_diff_key($trygroups, array_flip($failedgroups)); // without previous failures $squarelinks[$targetsquare][] = $circleid;
$targetgroup = $this->get_element_with_lowest_workload($trygroups); $squareworkload[$targetsquare]++;
} $o[] = 'debug::indent::increasing square workload to ' . $squareworkload[$targetsquare];
if ($targetgroup === false) { if ($targetgroup) {
$keeptrying = false; // recalculate the group workload
$o[] = 'error::indent::Not enough peers available'; // todo translate $squaregroupsworkload[$targetgroup] = 0;
break; foreach ($allsquares[$targetgroup] as $squareid => $square) {
} $squaregroupsworkload[$targetgroup] += $squareworkload[$squareid];
$o[] = 'debug::indent::next square should be from group id ' . $targetgroup; }
// now, choose a square from the target group $squaregroupsworkload[$targetgroup] /= count($allsquares[$targetgroup]);
$trysquares = array_intersect_key($squareworkload, $allsquares[$targetgroup]); $o[] = 'debug::indent::increasing group workload to ' . $squaregroupsworkload[$targetgroup];
// $o[] = 'debug::indent::individual workloads in this group are ' . json_encode($trysquares);
unset($trysquares[$circleid]); // can't allocate to self
$trysquares = array_diff_key($trysquares, array_flip($circlelinks[$circleid])); // can't re-allocate the same
$targetsquare = $this->get_element_with_lowest_workload($trysquares);
if (false === $targetsquare) {
$o[] = 'debug::indent::unable to find an available square. trying another group';
$failedgroups[] = $targetgroup;
continue;
}
$o[] = 'debug::indent::target square = ' . $targetsquare;
// ok - we have found the square
$circlelinks[$circleid][] = $targetsquare;
$squarelinks[$targetsquare][] = $circleid;
$squareworkload[$targetsquare]++;
$o[] = 'debug::indent::increasing square workload to ' . $squareworkload[$targetsquare];
if ($targetgroup) {
// recalculate the group workload
$squaregroupsworkload[$targetgroup] = 0;
foreach ($allsquares[$targetgroup] as $squareid => $square) {
$squaregroupsworkload[$targetgroup] += $squareworkload[$squareid];
} }
$squaregroupsworkload[$targetgroup] /= count($allsquares[$targetgroup]); } // end of processing this circle
$o[] = 'debug::indent::increasing group workload to ' . $squaregroupsworkload[$targetgroup]; } // end of one iteration of processing circles in the group
} } // end of all iterations over circles in the group
} // end of processing this circle
} // end of processing circles in the group
} // end of processing circle groups } // end of processing circle groups
$returned = array(); $returned = array();
if (self::USERTYPE_AUTHOR == $numper) { if (self::USERTYPE_AUTHOR == $numper) {
@ -569,15 +575,14 @@ class workshop_random_allocator implements workshop_allocator {
/** /**
* Shuffle the order of array elements preserving the key=>values * Shuffle the order of array elements preserving the key=>values
* *
* @author rich at home dot nl
* @link http://php.net/manual/en/function.shuffle.php#80586
* @param array $array to be shuffled * @param array $array to be shuffled
* @return true * @return true
*/ */
protected function shuffle_assoc(&$array) { protected function shuffle_assoc(&$array) {
if (count($array) > 1) { if (count($array) > 1) {
// $keys needs to be an array, no need to shuffle 1 item or empty arrays, anyway // $keys needs to be an array, no need to shuffle 1 item or empty arrays, anyway
$keys = array_rand($array, count($array)); $keys = array_keys($array);
shuffle($keys);
foreach($keys as $key) { foreach($keys as $key) {
$new[$key] = $array[$key]; $new[$key] = $array[$key];
} }