001package org.cpsolver.studentsct.online.selection; 002 003import java.util.Hashtable; 004import java.util.Set; 005 006import org.cpsolver.ifs.assignment.Assignment; 007import org.cpsolver.studentsct.model.CourseRequest; 008import org.cpsolver.studentsct.model.Enrollment; 009import org.cpsolver.studentsct.model.FreeTimeRequest; 010import org.cpsolver.studentsct.model.Request; 011import org.cpsolver.studentsct.model.Section; 012import org.cpsolver.studentsct.model.Student; 013import org.cpsolver.studentsct.model.Subpart; 014import org.cpsolver.studentsct.online.OnlineSectioningModel; 015 016/** 017* Equal weighting multi-criteria selection criterion. Much like the {@link OnlineSectioningCriterion}, but 018* course request priorities are ignored. Most complete solution is preferred instead. 019* 020* @version StudentSct 1.3 (Student Sectioning)<br> 021* Copyright (C) 2014 Tomas Muller<br> 022* <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 023* <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 024* <br> 025* This library is free software; you can redistribute it and/or modify 026* it under the terms of the GNU Lesser General Public License as 027* published by the Free Software Foundation; either version 3 of the 028* License, or (at your option) any later version. <br> 029* <br> 030* This library is distributed in the hope that it will be useful, but 031* WITHOUT ANY WARRANTY; without even the implied warranty of 032* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 033* Lesser General Public License for more details. <br> 034* <br> 035* You should have received a copy of the GNU Lesser General Public 036* License along with this library; if not see <a 037* href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>. 038* 039*/ 040public class EqualWeightCriterion extends OnlineSectioningCriterion { 041 042 public EqualWeightCriterion(Student student, OnlineSectioningModel model, 043 Assignment<Request, Enrollment> assignment, Hashtable<CourseRequest, Set<Section>> preferredSections) { 044 super(student, model, assignment, preferredSections); 045 } 046 047 @Override 048 public int compare(Assignment<Request, Enrollment> assignment, Enrollment[] current, Enrollment[] best) { 049 if (best == null) 050 return -1; 051 052 // 0. best number of assigned course requests (including alternativity & 053 // priority) 054 int currentAssignedCourseReq = 0, bestAssignedCourseReq = 0; 055 int currentAssignedRequests = 0, bestAssignedRequests = 0; 056 int currentAssignedPriority = 0, bestAssignedPriority = 0; 057 int currentAssignedAlternativity = 0, bestAssignedAlternativity = 0; 058 for (int idx = 0; idx < current.length; idx++) { 059 if (current[idx] != null && current[idx].getAssignments() != null) { 060 currentAssignedRequests++; 061 if (current[idx].isCourseRequest()) 062 currentAssignedCourseReq++; 063 currentAssignedPriority += current[idx].getPriority() * current[idx].getPriority(); 064 currentAssignedAlternativity += (current[idx].getRequest().isAlternative() ? 1 : 0); 065 } 066 if (best[idx] != null && best[idx].getAssignments() != null) { 067 bestAssignedRequests++; 068 if (best[idx].isCourseRequest()) 069 bestAssignedCourseReq++; 070 bestAssignedPriority += best[idx].getPriority() * best[idx].getPriority(); 071 bestAssignedAlternativity += (best[idx].getRequest().isAlternative() ? 1 : 0); 072 } 073 } 074 if (currentAssignedCourseReq > bestAssignedCourseReq) 075 return -1; 076 if (bestAssignedCourseReq > currentAssignedCourseReq) 077 return 1; 078 if (currentAssignedPriority < bestAssignedPriority) 079 return -1; 080 if (bestAssignedPriority < currentAssignedPriority) 081 return 1; 082 if (currentAssignedAlternativity < bestAssignedAlternativity) 083 return -1; 084 if (bestAssignedAlternativity < currentAssignedAlternativity) 085 return 1; 086 087 // 0.5. avoid course overlaps 088 if (getModel().getTimeOverlaps() != null) { 089 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 090 for (int idx = 0; idx < current.length; idx++) { 091 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest) { 092 for (int x = 0; x < idx; x++) { 093 if (best[x] != null && best[x].getAssignments() != null 094 && best[x].getRequest() instanceof CourseRequest) 095 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 096 } 097 } 098 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest) { 099 for (int x = 0; x < idx; x++) { 100 if (current[x] != null && current[x].getAssignments() != null 101 && current[x].getRequest() instanceof CourseRequest) 102 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 103 } 104 } 105 } 106 if (currentTimeOverlaps < bestTimeOverlaps) 107 return -1; 108 if (bestTimeOverlaps < currentTimeOverlaps) 109 return 1; 110 } 111 112 // 1. minimize number of penalties 113 double bestPenalties = 0, currentPenalties = 0; 114 for (int idx = 0; idx < current.length; idx++) { 115 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 116 for (Section section : best[idx].getSections()) 117 bestPenalties += getModel().getOverExpected(assignment, section, best[idx].getRequest()); 118 } 119 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 120 for (Section section : current[idx].getSections()) 121 currentPenalties += getModel().getOverExpected(assignment, section, current[idx].getRequest()); 122 } 123 } 124 if (currentPenalties < bestPenalties) 125 return -1; 126 if (bestPenalties < currentPenalties) 127 return 1; 128 129 // 2. best number of assigned requests (including free time requests) 130 if (currentAssignedRequests > bestAssignedRequests) 131 return -1; 132 if (bestAssignedRequests > currentAssignedRequests) 133 return 1; 134 135 // 3. maximize selection 136 int bestSelected = 0, currentSelected = 0; 137 for (int idx = 0; idx < current.length; idx++) { 138 Set<Section> preferred = getPreferredSections(getRequest(idx)); 139 if (preferred != null && !preferred.isEmpty()) { 140 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 141 for (Section section : best[idx].getSections()) 142 if (preferred.contains(section)) 143 bestSelected++; 144 } 145 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 146 for (Section section : current[idx].getSections()) 147 if (preferred.contains(section)) 148 currentSelected++; 149 } 150 } 151 } 152 if (currentSelected > bestSelected) 153 return -1; 154 if (bestSelected > currentSelected) 155 return 1; 156 157 // 3.5 maximize preferences 158 double bestSelectedConfigs = 0, currentSelectedConfigs = 0; 159 double bestSelectedSections = 0, currentSelectedSections = 0; 160 for (int idx = 0; idx < current.length; idx++) { 161 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 162 bestSelectedSections += best[idx].percentSelectedSameSection(); 163 bestSelectedConfigs += best[idx].percentSelectedSameConfig(); 164 } 165 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 166 currentSelectedSections += current[idx].percentSelectedSameSection(); 167 currentSelectedConfigs += current[idx].percentSelectedSameConfig(); 168 } 169 } 170 if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections) return -1; 171 if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections) return 1; 172 173 // 4. avoid time overlaps 174 if (getModel().getTimeOverlaps() != null) { 175 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 176 for (int idx = 0; idx < current.length; idx++) { 177 if (best[idx] != null && best[idx].getAssignments() != null) { 178 for (int x = 0; x < idx; x++) { 179 if (best[x] != null && best[x].getAssignments() != null) 180 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 181 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 182 bestTimeOverlaps += getModel().getTimeOverlaps() 183 .nrConflicts( 184 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 185 best[idx]); 186 } 187 } 188 if (current[idx] != null && current[idx].getAssignments() != null) { 189 for (int x = 0; x < idx; x++) { 190 if (current[x] != null && current[x].getAssignments() != null) 191 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 192 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 193 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts( 194 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 195 current[idx]); 196 } 197 } 198 } 199 if (currentTimeOverlaps < bestTimeOverlaps) 200 return -1; 201 if (bestTimeOverlaps < currentTimeOverlaps) 202 return 1; 203 } 204 205 // 5. avoid distance conflicts 206 if (getModel().getDistanceConflict() != null) { 207 int bestDistanceConf = 0, currentDistanceConf = 0; 208 for (int idx = 0; idx < current.length; idx++) { 209 if (best[idx] != null && best[idx].getAssignments() != null) { 210 for (int x = 0; x < idx; x++) { 211 if (best[x] != null && best[x].getAssignments() != null) 212 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 213 } 214 } 215 if (current[idx] != null && current[idx].getAssignments() != null) { 216 for (int x = 0; x < idx; x++) { 217 if (current[x] != null && current[x].getAssignments() != null) 218 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 219 current[idx]); 220 } 221 } 222 } 223 if (currentDistanceConf < bestDistanceConf) 224 return -1; 225 if (bestDistanceConf < currentDistanceConf) 226 return 1; 227 } 228 229 // 6. avoid no-time sections 230 int bestNoTime = 0, currentNoTime = 0; 231 for (int idx = 0; idx < current.length; idx++) { 232 if (best[idx] != null && best[idx].getAssignments() != null) { 233 for (Section section : best[idx].getSections()) 234 if (section.getTime() == null) 235 bestNoTime++; 236 } 237 if (current[idx] != null && current[idx].getAssignments() != null) { 238 for (Section section : current[idx].getSections()) 239 if (section.getTime() == null) 240 currentNoTime++; 241 } 242 } 243 if (currentNoTime < bestNoTime) 244 return -1; 245 if (bestNoTime < currentNoTime) 246 return 1; 247 248 // 7. balance sections 249 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 250 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 251 for (int idx = 0; idx < current.length; idx++) { 252 if (best[idx] != null && best[idx].getAssignments() != null) { 253 for (Section section : best[idx].getSections()) { 254 Subpart subpart = section.getSubpart(); 255 // skip unlimited and single section subparts 256 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 257 continue; 258 // average size 259 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 260 // section is below average 261 if (section.getLimit() < averageSize) 262 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 263 bestAltSectionsWithLimit++; 264 } 265 } 266 if (current[idx] != null && current[idx].getAssignments() != null) { 267 for (Section section : current[idx].getSections()) { 268 Subpart subpart = section.getSubpart(); 269 // skip unlimited and single section subparts 270 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 271 continue; 272 // average size 273 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 274 // section is below average 275 if (section.getLimit() < averageSize) 276 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 277 currentAltSectionsWithLimit++; 278 } 279 } 280 } 281 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 282 : 0.0); 283 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 284 / currentAltSectionsWithLimit : 0.0); 285 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 286 return -1; 287 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 288 return 1; 289 290 // 8. average penalty sections 291 double bestPenalty = 0.0, currentPenalty = 0.0; 292 for (int idx = 0; idx < current.length; idx++) { 293 if (best[idx] != null && best[idx].getAssignments() != null) { 294 for (Section section : best[idx].getSections()) 295 bestPenalty += section.getPenalty(); 296 } 297 if (current[idx] != null && current[idx].getAssignments() != null) { 298 for (Section section : current[idx].getSections()) 299 currentPenalty += section.getPenalty(); 300 } 301 } 302 if (currentPenalty < bestPenalty) 303 return -1; 304 if (bestPenalty < currentPenalty) 305 return 1; 306 307 return 0; 308 } 309 310 @Override 311 public boolean canImprove(Assignment<Request, Enrollment> assignment, int maxIdx, Enrollment[] current, 312 Enrollment[] best) { 313 // 0. best number of assigned course requests (including alternativity & 314 // priority) 315 int currentAssignedCourseReq = 0, bestAssignedCourseReq = 0; 316 int currentAssignedRequests = 0, bestAssignedRequests = 0; 317 int currentAssignedPriority = 0, bestAssignedPriority = 0; 318 int currentAssignedAlternativity = 0, bestAssignedAlternativity = 0; 319 int alt = 0; 320 for (int idx = 0; idx < current.length; idx++) { 321 if (idx < maxIdx) { 322 if (current[idx] != null && current[idx].getAssignments() != null) { 323 currentAssignedRequests++; 324 if (current[idx].isCourseRequest()) 325 currentAssignedCourseReq++; 326 currentAssignedPriority += current[idx].getPriority() * current[idx].getPriority(); 327 currentAssignedAlternativity += (current[idx].getRequest().isAlternative() ? 1 : 0); 328 } else if (!isFreeTime(idx) && !getRequest(idx).isAlternative()) { 329 alt++; 330 } 331 } else { 332 if (!getRequest(idx).isAlternative()) { 333 currentAssignedRequests++; 334 if (!isFreeTime(idx)) 335 currentAssignedCourseReq++; 336 } else if (alt > 0) { 337 currentAssignedRequests++; 338 currentAssignedCourseReq++; 339 alt--; 340 currentAssignedAlternativity++; 341 } 342 } 343 if (best[idx] != null && best[idx].getAssignments() != null) { 344 bestAssignedRequests++; 345 if (best[idx].isCourseRequest()) 346 bestAssignedCourseReq++; 347 bestAssignedPriority += best[idx].getPriority() * best[idx].getPriority(); 348 bestAssignedAlternativity += (best[idx].getRequest().isAlternative() ? 1 : 0); 349 } 350 } 351 if (currentAssignedCourseReq > bestAssignedCourseReq) 352 return true; 353 if (bestAssignedCourseReq > currentAssignedCourseReq) 354 return false; 355 if (currentAssignedPriority < bestAssignedPriority) 356 return true; 357 if (bestAssignedPriority < currentAssignedPriority) 358 return false; 359 if (currentAssignedAlternativity < bestAssignedAlternativity) 360 return true; 361 if (bestAssignedAlternativity < currentAssignedAlternativity) 362 return false; 363 364 // 0.5. avoid course time overlaps 365 if (getModel().getTimeOverlaps() != null) { 366 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 367 for (int idx = 0; idx < current.length; idx++) { 368 if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) { 369 for (int x = 0; x < idx; x++) { 370 if (best[x] != null && best[x].getRequest() instanceof CourseRequest) 371 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 372 } 373 } 374 if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) { 375 for (int x = 0; x < idx; x++) { 376 if (current[x] != null && current[x].getRequest() instanceof CourseRequest) 377 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 378 } 379 } 380 } 381 if (currentTimeOverlaps < bestTimeOverlaps) 382 return true; 383 if (bestTimeOverlaps < currentTimeOverlaps) 384 return false; 385 } 386 387 // 1. maximize number of penalties 388 double bestPenalties = 0, currentPenalties = 0; 389 for (int idx = 0; idx < current.length; idx++) { 390 if (best[idx] != null) { 391 for (Section section : best[idx].getSections()) 392 bestPenalties += getModel().getOverExpected(assignment, section, best[idx].getRequest()); 393 } 394 if (current[idx] != null && idx < maxIdx) { 395 for (Section section : current[idx].getSections()) 396 currentPenalties += getModel().getOverExpected(assignment, section, current[idx].getRequest()); 397 } 398 } 399 if (currentPenalties < bestPenalties) 400 return true; 401 if (bestPenalties < currentPenalties) 402 return false; 403 404 // 2. best number of assigned requests (including free time requests) 405 if (currentAssignedRequests > bestAssignedRequests) 406 return true; 407 if (bestAssignedRequests > currentAssignedRequests) 408 return false; 409 410 // 3. maximize selection 411 int bestSelected = 0, currentSelected = 0; 412 for (int idx = 0; idx < current.length; idx++) { 413 if (best[idx] != null && best[idx].isCourseRequest()) { 414 Set<Section> preferred = getPreferredSections(best[idx].getRequest()); 415 if (preferred != null && !preferred.isEmpty()) { 416 for (Section section : best[idx].getSections()) 417 if (preferred.contains(section)) { 418 if (idx < maxIdx) 419 bestSelected++; 420 } else if (idx >= maxIdx) 421 bestSelected--; 422 } 423 } 424 if (current[idx] != null && idx < maxIdx && current[idx].isCourseRequest()) { 425 Set<Section> preferred = getPreferredSections(current[idx].getRequest()); 426 if (preferred != null && !preferred.isEmpty()) { 427 for (Section section : current[idx].getSections()) 428 if (preferred.contains(section)) 429 currentSelected++; 430 } 431 } 432 } 433 if (currentSelected > bestSelected) 434 return true; 435 if (bestSelected > currentSelected) 436 return false; 437 438 // 3.5 maximize preferences 439 double bestSelectedConfigs = 0, currentSelectedConfigs = 0; 440 double bestSelectedSections = 0, currentSelectedSections = 0; 441 for (int idx = 0; idx < current.length; idx++) { 442 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 443 bestSelectedSections += best[idx].percentSelectedSameSection(); 444 bestSelectedConfigs += best[idx].percentSelectedSameConfig(); 445 if (idx >= maxIdx) { 446 bestSelectedSections -= 1.0; 447 bestSelectedConfigs -= 1.0; 448 } 449 } 450 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 451 currentSelectedSections += current[idx].percentSelectedSameSection(); 452 currentSelectedConfigs += current[idx].percentSelectedSameConfig(); 453 } 454 } 455 if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections) return true; 456 if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections) return false; 457 458 // 4. avoid time overlaps 459 if (getModel().getTimeOverlaps() != null) { 460 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 461 for (int idx = 0; idx < current.length; idx++) { 462 if (best[idx] != null) { 463 for (int x = 0; x < idx; x++) { 464 if (best[x] != null) 465 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 466 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 467 bestTimeOverlaps += getModel().getTimeOverlaps() 468 .nrConflicts( 469 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 470 best[idx]); 471 } 472 } 473 if (current[idx] != null && idx < maxIdx) { 474 for (int x = 0; x < idx; x++) { 475 if (current[x] != null) 476 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 477 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 478 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts( 479 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 480 current[idx]); 481 } 482 } 483 } 484 if (currentTimeOverlaps < bestTimeOverlaps) 485 return true; 486 if (bestTimeOverlaps < currentTimeOverlaps) 487 return false; 488 } 489 490 // 5. avoid distance conflicts 491 if (getModel().getDistanceConflict() != null) { 492 int bestDistanceConf = 0, currentDistanceConf = 0; 493 for (int idx = 0; idx < current.length; idx++) { 494 if (best[idx] != null) { 495 for (int x = 0; x < idx; x++) { 496 if (best[x] != null) 497 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 498 } 499 } 500 if (current[idx] != null && idx < maxIdx) { 501 for (int x = 0; x < idx; x++) { 502 if (current[x] != null) 503 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 504 current[idx]); 505 } 506 } 507 } 508 if (currentDistanceConf < bestDistanceConf) 509 return true; 510 if (bestDistanceConf < currentDistanceConf) 511 return false; 512 } 513 514 // 6. avoid no-time sections 515 int bestNoTime = 0, currentNoTime = 0; 516 for (int idx = 0; idx < current.length; idx++) { 517 if (best[idx] != null) { 518 for (Section section : best[idx].getSections()) 519 if (section.getTime() == null) 520 bestNoTime++; 521 } 522 if (current[idx] != null && idx < maxIdx) { 523 for (Section section : current[idx].getSections()) 524 if (section.getTime() == null) 525 currentNoTime++; 526 } 527 } 528 if (currentNoTime < bestNoTime) 529 return true; 530 if (bestNoTime < currentNoTime) 531 return false; 532 533 // 7. balance sections 534 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 535 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 536 for (int idx = 0; idx < current.length; idx++) { 537 if (best[idx] != null) { 538 for (Section section : best[idx].getSections()) { 539 Subpart subpart = section.getSubpart(); 540 // skip unlimited and single section subparts 541 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 542 continue; 543 // average size 544 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 545 // section is below average 546 if (section.getLimit() < averageSize) 547 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 548 bestAltSectionsWithLimit++; 549 } 550 } 551 if (current[idx] != null && idx < maxIdx) { 552 for (Section section : current[idx].getSections()) { 553 Subpart subpart = section.getSubpart(); 554 // skip unlimited and single section subparts 555 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 556 continue; 557 // average size 558 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 559 // section is below average 560 if (section.getLimit() < averageSize) 561 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 562 currentAltSectionsWithLimit++; 563 } 564 } 565 } 566 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 567 : 0.0); 568 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 569 / currentAltSectionsWithLimit : 0.0); 570 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 571 return true; 572 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 573 return false; 574 575 // 8. average penalty sections 576 double bestPenalty = 0.0, currentPenalty = 0.0; 577 for (int idx = 0; idx < current.length; idx++) { 578 if (best[idx] != null) { 579 for (Section section : best[idx].getSections()) 580 bestPenalty += section.getPenalty(); 581 if (idx >= maxIdx && best[idx].isCourseRequest()) 582 bestPenalty -= ((CourseRequest) best[idx].getRequest()).getMinPenalty(); 583 } 584 if (current[idx] != null && idx < maxIdx) { 585 for (Section section : current[idx].getSections()) 586 currentPenalty += section.getPenalty(); 587 } 588 } 589 if (currentPenalty < bestPenalty) 590 return true; 591 if (bestPenalty < currentPenalty) 592 return false; 593 594 return true; 595 } 596}