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 & unavailabilities 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 for (int idx = 0; idx < current.length; idx++) { 107 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 108 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 109 } 110 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 111 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 112 } 113 } 114 if (currentTimeOverlaps < bestTimeOverlaps) 115 return -1; 116 if (bestTimeOverlaps < currentTimeOverlaps) 117 return 1; 118 } 119 120 // 1. minimize number of penalties 121 double bestPenalties = 0, currentPenalties = 0; 122 for (int idx = 0; idx < current.length; idx++) { 123 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 124 for (Section section : best[idx].getSections()) 125 bestPenalties += getModel().getOverExpected(assignment, section, best[idx].getRequest()); 126 } 127 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 128 for (Section section : current[idx].getSections()) 129 currentPenalties += getModel().getOverExpected(assignment, section, current[idx].getRequest()); 130 } 131 } 132 if (currentPenalties < bestPenalties) 133 return -1; 134 if (bestPenalties < currentPenalties) 135 return 1; 136 137 // 2. best number of assigned requests (including free time requests) 138 if (currentAssignedRequests > bestAssignedRequests) 139 return -1; 140 if (bestAssignedRequests > currentAssignedRequests) 141 return 1; 142 143 // 3. maximize selection 144 int bestSelected = 0, currentSelected = 0; 145 for (int idx = 0; idx < current.length; idx++) { 146 Set<Section> preferred = getPreferredSections(getRequest(idx)); 147 if (preferred != null && !preferred.isEmpty()) { 148 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 149 for (Section section : best[idx].getSections()) 150 if (preferred.contains(section)) 151 bestSelected++; 152 } 153 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 154 for (Section section : current[idx].getSections()) 155 if (preferred.contains(section)) 156 currentSelected++; 157 } 158 } 159 } 160 if (currentSelected > bestSelected) 161 return -1; 162 if (bestSelected > currentSelected) 163 return 1; 164 165 // 3.5 maximize preferences 166 double bestSelectedConfigs = 0, currentSelectedConfigs = 0; 167 double bestSelectedSections = 0, currentSelectedSections = 0; 168 for (int idx = 0; idx < current.length; idx++) { 169 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 170 bestSelectedSections += best[idx].percentSelectedSameSection(); 171 bestSelectedConfigs += best[idx].percentSelectedSameConfig(); 172 } 173 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 174 currentSelectedSections += current[idx].percentSelectedSameSection(); 175 currentSelectedConfigs += current[idx].percentSelectedSameConfig(); 176 } 177 } 178 if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections) return -1; 179 if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections) return 1; 180 181 // 4. avoid time overlaps 182 if (getModel().getTimeOverlaps() != null) { 183 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 184 for (int idx = 0; idx < current.length; idx++) { 185 if (best[idx] != null && best[idx].getAssignments() != null) { 186 for (int x = 0; x < idx; x++) { 187 if (best[x] != null && best[x].getAssignments() != null) 188 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 189 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 190 bestTimeOverlaps += getModel().getTimeOverlaps() 191 .nrConflicts( 192 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 193 best[idx]); 194 } 195 } 196 if (current[idx] != null && current[idx].getAssignments() != null) { 197 for (int x = 0; x < idx; x++) { 198 if (current[x] != null && current[x].getAssignments() != null) 199 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 200 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 201 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts( 202 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 203 current[idx]); 204 } 205 } 206 } 207 for (int idx = 0; idx < current.length; idx++) { 208 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 209 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 210 } 211 if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 212 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 213 } 214 } 215 if (currentTimeOverlaps < bestTimeOverlaps) 216 return -1; 217 if (bestTimeOverlaps < currentTimeOverlaps) 218 return 1; 219 } 220 221 // 5. avoid distance conflicts 222 if (getModel().getDistanceConflict() != null) { 223 int bestDistanceConf = 0, currentDistanceConf = 0; 224 for (int idx = 0; idx < current.length; idx++) { 225 if (best[idx] != null && best[idx].getAssignments() != null) { 226 for (int x = 0; x < idx; x++) { 227 if (best[x] != null && best[x].getAssignments() != null) 228 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 229 } 230 } 231 if (current[idx] != null && current[idx].getAssignments() != null) { 232 for (int x = 0; x < idx; x++) { 233 if (current[x] != null && current[x].getAssignments() != null) 234 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 235 current[idx]); 236 } 237 } 238 } 239 if (currentDistanceConf < bestDistanceConf) 240 return -1; 241 if (bestDistanceConf < currentDistanceConf) 242 return 1; 243 } 244 245 // 6. avoid no-time sections 246 int bestNoTime = 0, currentNoTime = 0; 247 for (int idx = 0; idx < current.length; idx++) { 248 if (best[idx] != null && best[idx].getAssignments() != null) { 249 for (Section section : best[idx].getSections()) 250 if (section.getTime() == null) 251 bestNoTime++; 252 } 253 if (current[idx] != null && current[idx].getAssignments() != null) { 254 for (Section section : current[idx].getSections()) 255 if (section.getTime() == null) 256 currentNoTime++; 257 } 258 } 259 if (currentNoTime < bestNoTime) 260 return -1; 261 if (bestNoTime < currentNoTime) 262 return 1; 263 264 // 7. balance sections 265 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 266 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 267 for (int idx = 0; idx < current.length; idx++) { 268 if (best[idx] != null && best[idx].getAssignments() != null) { 269 for (Section section : best[idx].getSections()) { 270 Subpart subpart = section.getSubpart(); 271 // skip unlimited and single section subparts 272 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 273 continue; 274 // average size 275 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 276 // section is below average 277 if (section.getLimit() < averageSize) 278 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 279 bestAltSectionsWithLimit++; 280 } 281 } 282 if (current[idx] != null && current[idx].getAssignments() != null) { 283 for (Section section : current[idx].getSections()) { 284 Subpart subpart = section.getSubpart(); 285 // skip unlimited and single section subparts 286 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 287 continue; 288 // average size 289 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 290 // section is below average 291 if (section.getLimit() < averageSize) 292 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 293 currentAltSectionsWithLimit++; 294 } 295 } 296 } 297 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 298 : 0.0); 299 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 300 / currentAltSectionsWithLimit : 0.0); 301 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 302 return -1; 303 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 304 return 1; 305 306 // 8. average penalty sections 307 double bestPenalty = 0.0, currentPenalty = 0.0; 308 for (int idx = 0; idx < current.length; idx++) { 309 if (best[idx] != null && best[idx].getAssignments() != null) { 310 for (Section section : best[idx].getSections()) 311 bestPenalty += section.getPenalty(); 312 } 313 if (current[idx] != null && current[idx].getAssignments() != null) { 314 for (Section section : current[idx].getSections()) 315 currentPenalty += section.getPenalty(); 316 } 317 } 318 if (currentPenalty < bestPenalty) 319 return -1; 320 if (bestPenalty < currentPenalty) 321 return 1; 322 323 return 0; 324 } 325 326 @Override 327 public boolean canImprove(Assignment<Request, Enrollment> assignment, int maxIdx, Enrollment[] current, 328 Enrollment[] best) { 329 // 0. best number of assigned course requests (including alternativity & 330 // priority) 331 int currentAssignedCourseReq = 0, bestAssignedCourseReq = 0; 332 int currentAssignedRequests = 0, bestAssignedRequests = 0; 333 int currentAssignedPriority = 0, bestAssignedPriority = 0; 334 int currentAssignedAlternativity = 0, bestAssignedAlternativity = 0; 335 int alt = 0; 336 for (int idx = 0; idx < current.length; idx++) { 337 if (idx < maxIdx) { 338 if (current[idx] != null && current[idx].getAssignments() != null) { 339 currentAssignedRequests++; 340 if (current[idx].isCourseRequest()) 341 currentAssignedCourseReq++; 342 currentAssignedPriority += current[idx].getPriority() * current[idx].getPriority(); 343 currentAssignedAlternativity += (current[idx].getRequest().isAlternative() ? 1 : 0); 344 } else if (!isFreeTime(idx) && !getRequest(idx).isAlternative()) { 345 alt++; 346 } 347 } else { 348 if (!getRequest(idx).isAlternative()) { 349 currentAssignedRequests++; 350 if (!isFreeTime(idx)) 351 currentAssignedCourseReq++; 352 } else if (alt > 0) { 353 currentAssignedRequests++; 354 currentAssignedCourseReq++; 355 alt--; 356 currentAssignedAlternativity++; 357 } 358 } 359 if (best[idx] != null && best[idx].getAssignments() != null) { 360 bestAssignedRequests++; 361 if (best[idx].isCourseRequest()) 362 bestAssignedCourseReq++; 363 bestAssignedPriority += best[idx].getPriority() * best[idx].getPriority(); 364 bestAssignedAlternativity += (best[idx].getRequest().isAlternative() ? 1 : 0); 365 } 366 } 367 if (currentAssignedCourseReq > bestAssignedCourseReq) 368 return true; 369 if (bestAssignedCourseReq > currentAssignedCourseReq) 370 return false; 371 if (currentAssignedPriority < bestAssignedPriority) 372 return true; 373 if (bestAssignedPriority < currentAssignedPriority) 374 return false; 375 if (currentAssignedAlternativity < bestAssignedAlternativity) 376 return true; 377 if (bestAssignedAlternativity < currentAssignedAlternativity) 378 return false; 379 380 // 0.5. avoid course time overlaps & unavailabilities 381 if (getModel().getTimeOverlaps() != null) { 382 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 383 for (int idx = 0; idx < current.length; idx++) { 384 if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) { 385 for (int x = 0; x < idx; x++) { 386 if (best[x] != null && best[x].getRequest() instanceof CourseRequest) 387 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 388 } 389 } 390 if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) { 391 for (int x = 0; x < idx; x++) { 392 if (current[x] != null && current[x].getRequest() instanceof CourseRequest) 393 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 394 } 395 } 396 } 397 for (int idx = 0; idx < current.length; idx++) { 398 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 399 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 400 } 401 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 402 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 403 } 404 } 405 if (currentTimeOverlaps < bestTimeOverlaps) 406 return true; 407 if (bestTimeOverlaps < currentTimeOverlaps) 408 return false; 409 } 410 411 // 1. maximize number of penalties 412 double bestPenalties = 0, currentPenalties = 0; 413 for (int idx = 0; idx < current.length; idx++) { 414 if (best[idx] != null) { 415 for (Section section : best[idx].getSections()) 416 bestPenalties += getModel().getOverExpected(assignment, section, best[idx].getRequest()); 417 } 418 if (current[idx] != null && idx < maxIdx) { 419 for (Section section : current[idx].getSections()) 420 currentPenalties += getModel().getOverExpected(assignment, section, current[idx].getRequest()); 421 } 422 } 423 if (currentPenalties < bestPenalties) 424 return true; 425 if (bestPenalties < currentPenalties) 426 return false; 427 428 // 2. best number of assigned requests (including free time requests) 429 if (currentAssignedRequests > bestAssignedRequests) 430 return true; 431 if (bestAssignedRequests > currentAssignedRequests) 432 return false; 433 434 // 3. maximize selection 435 int bestSelected = 0, currentSelected = 0; 436 for (int idx = 0; idx < current.length; idx++) { 437 if (best[idx] != null && best[idx].isCourseRequest()) { 438 Set<Section> preferred = getPreferredSections(best[idx].getRequest()); 439 if (preferred != null && !preferred.isEmpty()) { 440 for (Section section : best[idx].getSections()) 441 if (preferred.contains(section)) { 442 if (idx < maxIdx) 443 bestSelected++; 444 } else if (idx >= maxIdx) 445 bestSelected--; 446 } 447 } 448 if (current[idx] != null && idx < maxIdx && current[idx].isCourseRequest()) { 449 Set<Section> preferred = getPreferredSections(current[idx].getRequest()); 450 if (preferred != null && !preferred.isEmpty()) { 451 for (Section section : current[idx].getSections()) 452 if (preferred.contains(section)) 453 currentSelected++; 454 } 455 } 456 } 457 if (currentSelected > bestSelected) 458 return true; 459 if (bestSelected > currentSelected) 460 return false; 461 462 // 3.5 maximize preferences 463 double bestSelectedConfigs = 0, currentSelectedConfigs = 0; 464 double bestSelectedSections = 0, currentSelectedSections = 0; 465 for (int idx = 0; idx < current.length; idx++) { 466 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 467 bestSelectedSections += best[idx].percentSelectedSameSection(); 468 bestSelectedConfigs += best[idx].percentSelectedSameConfig(); 469 if (idx >= maxIdx) { 470 bestSelectedSections -= 1.0; 471 bestSelectedConfigs -= 1.0; 472 } 473 } 474 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 475 currentSelectedSections += current[idx].percentSelectedSameSection(); 476 currentSelectedConfigs += current[idx].percentSelectedSameConfig(); 477 } 478 } 479 if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections) return true; 480 if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections) return false; 481 482 // 4. avoid time overlaps 483 if (getModel().getTimeOverlaps() != null) { 484 int bestTimeOverlaps = 0, currentTimeOverlaps = 0; 485 for (int idx = 0; idx < current.length; idx++) { 486 if (best[idx] != null) { 487 for (int x = 0; x < idx; x++) { 488 if (best[x] != null) 489 bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]); 490 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 491 bestTimeOverlaps += getModel().getTimeOverlaps() 492 .nrConflicts( 493 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 494 best[idx]); 495 } 496 } 497 if (current[idx] != null && idx < maxIdx) { 498 for (int x = 0; x < idx; x++) { 499 if (current[x] != null) 500 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]); 501 else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest) 502 currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts( 503 ((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), 504 current[idx]); 505 } 506 } 507 } 508 for (int idx = 0; idx < current.length; idx++) { 509 if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) { 510 bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]); 511 } 512 if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) { 513 currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]); 514 } 515 } 516 if (currentTimeOverlaps < bestTimeOverlaps) 517 return true; 518 if (bestTimeOverlaps < currentTimeOverlaps) 519 return false; 520 } 521 522 // 5. avoid distance conflicts 523 if (getModel().getDistanceConflict() != null) { 524 int bestDistanceConf = 0, currentDistanceConf = 0; 525 for (int idx = 0; idx < current.length; idx++) { 526 if (best[idx] != null) { 527 for (int x = 0; x < idx; x++) { 528 if (best[x] != null) 529 bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]); 530 } 531 } 532 if (current[idx] != null && idx < maxIdx) { 533 for (int x = 0; x < idx; x++) { 534 if (current[x] != null) 535 currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], 536 current[idx]); 537 } 538 } 539 } 540 if (currentDistanceConf < bestDistanceConf) 541 return true; 542 if (bestDistanceConf < currentDistanceConf) 543 return false; 544 } 545 546 // 6. avoid no-time sections 547 int bestNoTime = 0, currentNoTime = 0; 548 for (int idx = 0; idx < current.length; idx++) { 549 if (best[idx] != null) { 550 for (Section section : best[idx].getSections()) 551 if (section.getTime() == null) 552 bestNoTime++; 553 } 554 if (current[idx] != null && idx < maxIdx) { 555 for (Section section : current[idx].getSections()) 556 if (section.getTime() == null) 557 currentNoTime++; 558 } 559 } 560 if (currentNoTime < bestNoTime) 561 return true; 562 if (bestNoTime < currentNoTime) 563 return false; 564 565 // 7. balance sections 566 double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0; 567 int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0; 568 for (int idx = 0; idx < current.length; idx++) { 569 if (best[idx] != null) { 570 for (Section section : best[idx].getSections()) { 571 Subpart subpart = section.getSubpart(); 572 // skip unlimited and single section subparts 573 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 574 continue; 575 // average size 576 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 577 // section is below average 578 if (section.getLimit() < averageSize) 579 bestUnavailableSize += (averageSize - section.getLimit()) / averageSize; 580 bestAltSectionsWithLimit++; 581 } 582 } 583 if (current[idx] != null && idx < maxIdx) { 584 for (Section section : current[idx].getSections()) { 585 Subpart subpart = section.getSubpart(); 586 // skip unlimited and single section subparts 587 if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0) 588 continue; 589 // average size 590 double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size(); 591 // section is below average 592 if (section.getLimit() < averageSize) 593 currentUnavailableSize += (averageSize - section.getLimit()) / averageSize; 594 currentAltSectionsWithLimit++; 595 } 596 } 597 } 598 double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit 599 : 0.0); 600 double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize 601 / currentAltSectionsWithLimit : 0.0); 602 if (currentUnavailableSizeFraction < bestUnavailableSizeFraction) 603 return true; 604 if (bestUnavailableSizeFraction < currentUnavailableSizeFraction) 605 return false; 606 607 // 8. average penalty sections 608 double bestPenalty = 0.0, currentPenalty = 0.0; 609 for (int idx = 0; idx < current.length; idx++) { 610 if (best[idx] != null) { 611 for (Section section : best[idx].getSections()) 612 bestPenalty += section.getPenalty(); 613 if (idx >= maxIdx && best[idx].isCourseRequest()) 614 bestPenalty -= ((CourseRequest) best[idx].getRequest()).getMinPenalty(); 615 } 616 if (current[idx] != null && idx < maxIdx) { 617 for (Section section : current[idx].getSections()) 618 currentPenalty += section.getPenalty(); 619 } 620 } 621 if (currentPenalty < bestPenalty) 622 return true; 623 if (bestPenalty < currentPenalty) 624 return false; 625 626 return true; 627 } 628}