001 package net.sf.cpsolver.coursett.heuristics; 002 003 import java.util.Enumeration; 004 import java.util.Vector; 005 006 import net.sf.cpsolver.ifs.util.FastVector; 007 008 /** 009 * General hierarchical selection. 010 * <br><br> 011 * We have implemented a hierarchical handling of the value selection criteria. There are three levels of comparison. At 012 * each level a weighted sum of the criteria described below is computed. Only solutions with the smallest sum are 013 * considered in the next level. The weights express how quickly a complete solution should be found. Only hard 014 * constraints are satisfied in the first level sum. Distance from the initial solution (MPP), and a weighting of 015 * major preferences (including time, classroom requirements and student conflicts), are considered in the next level. 016 * In the third level, other minor criteria are considered. In general, a criterion can be used in more than one level, 017 * e.g., with different weights. 018 * <br><br> 019 * The above sums order the values lexicographically: the best value having the smallest first level sum, the smallest 020 * second level sum among values with the smallest first level sum, and the smallest third level sum among these values. 021 * As mentioned above, this allows diversification between the importance of individual criteria. 022 * <br><br> 023 * Furthermore, the value selection heuristics also support some limits (e.g., that all values with a first level sum 024 * smaller than a given percentage Pth above the best value [typically 10%] will go to the second level comparison 025 * and so on). This allows for the continued feasibility of a value near to the best that may yet be much better in the 026 * next level of comparison. If there is more than one solution after these three levels of comparison, one is 027 * selected randomly. This approach helped us to significantly improve the quality of the resultant solutions. 028 * <br><br> 029 * In general, there can be more than three levels of these weighted sums, however three of them seem to be sufficient for 030 * spreading weights of various criteria for our problem. 031 * 032 * @see PlacementSelection 033 * @version 034 * CourseTT 1.1 (University Course Timetabling)<br> 035 * Copyright (C) 2006 Tomáš Müller<br> 036 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 037 * Lazenska 391, 76314 Zlin, Czech Republic<br> 038 * <br> 039 * This library is free software; you can redistribute it and/or 040 * modify it under the terms of the GNU Lesser General Public 041 * License as published by the Free Software Foundation; either 042 * version 2.1 of the License, or (at your option) any later version. 043 * <br><br> 044 * This library is distributed in the hope that it will be useful, 045 * but WITHOUT ANY WARRANTY; without even the implied warranty of 046 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 047 * Lesser General Public License for more details. 048 * <br><br> 049 * You should have received a copy of the GNU Lesser General Public 050 * License along with this library; if not, write to the Free Software 051 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 052 */ 053 public class HeuristicSelector { 054 private double[] iThreshKoef; 055 private Vector iElements = new FastVector(); 056 private double iBestValueZero = 0.0; 057 058 /** Constructor 059 * @param threshKoef limit for each level, e.g., new double[] {0.1, 0.1, 0.1} for three level selection with 10% limit on each level 060 */ 061 public HeuristicSelector(double[] threshKoef) { 062 iThreshKoef = threshKoef; 063 } 064 065 /** Adds an object to selection 066 * @param values weighted sum for each level 067 * @param object object to be returned if selected 068 * @return true if added (it is not added if it is obvious that it cannot be selected) 069 */ 070 public boolean add(double[] values, Object object) { 071 if (iElements.isEmpty() || values[0] < iBestValueZero) { 072 iBestValueZero = values[0]; 073 iElements.addElement(new Element(values, object)); 074 return true; 075 } else if (values[0] <= iBestValueZero * (iBestValueZero<0.0?1.0-iThreshKoef[0]:1.0+iThreshKoef[0])) { 076 iElements.addElement(new Element(values, object)); 077 return true; 078 } 079 return false; 080 } 081 082 public Double firstLevelThreshold() { 083 return (iElements.isEmpty()?null:new Double(iBestValueZero * (iBestValueZero<0.0?1.0-iThreshKoef[0]:1.0+iThreshKoef[0]))); 084 } 085 086 /** Do the selection. 087 * @return inserted objects which met the criteria 088 */ 089 public Vector selection() { 090 Vector selection = iElements; 091 double bestValue = iBestValueZero; 092 for (int level=0; level<iThreshKoef.length; level++) { 093 Vector x = new FastVector(selection.size()); 094 double threshold = (bestValue<0.0?1.0-iThreshKoef[level]:1.0+iThreshKoef[level]) * bestValue; 095 //System.out.println("B"+(level+1)+": "+bestValue); 096 //System.out.println("T"+(level+1)+": "+threshold); 097 double nextBestValue = 0.0; 098 boolean lastLevel = (level+1==iThreshKoef.length); 099 for (Enumeration e=selection.elements();e.hasMoreElements();) { 100 Element element = (Element)e.nextElement(); 101 if (element.getValue(level)<=threshold) { 102 if (!lastLevel && (x.isEmpty() || element.getValue(level+1)<nextBestValue)) 103 nextBestValue=element.getValue(level+1); 104 x.addElement(element); 105 } 106 } 107 selection = x; 108 bestValue = nextBestValue; 109 } 110 return selection; 111 } 112 113 /** An element in heuristical selection*/ 114 public class Element { 115 private double[] iValues; 116 private Object iObject; 117 private Element(double[] values, Object object) { 118 iValues = values; iObject = object; 119 } 120 /** weighted sum in each level*/ 121 public double[] getValues() { return iValues; } 122 /** weighted sum in the given level*/ 123 public double getValue(int level) { return iValues[level]; } 124 /** given object */ 125 public Object getObject() { return iObject; } 126 public String toString() { 127 StringBuffer sb = new StringBuffer(); 128 for (int i=0;i<iValues.length;i++) sb.append(i==0?"":",").append(iValues[i]); 129 return "["+sb+"]:"+iObject; 130 } 131 } 132 }