/** * */ package vroom.trsp.datamodel; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import umontreal.iro.lecuyer.rng.RandomStream; /** * <code>GroerSolutionHasher</code> is an implementation for {@link TRSPSolution} of the hashing procedure presented in: * <p> * Groer, C.; Golden, B. & Wasil, E. <br/> * A library of local search heuristics for the vehicle routing problem <br/> * Mathematical Programming Computation, Springer Berlin / Heidelberg, 2010, 2, 79-101 * </p> * <p> * Creation date: May 23, 2011 - 10:52:45 AM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class GroerSolutionHasher implements ITRSPSolutionHasher { private final static Comparator<TRSPTour> TOUR_COMPARATOR = new Comparator<TRSPTour>() { @Override public int compare(TRSPTour o1, TRSPTour o2) { int n1 = o1.length() > 1 ? o1 .getNodeAt(1) : o1 .getTechnicianId(); int n2 = o2.length() > 1 ? o2 .getNodeAt(1) : o2 .getTechnicianId(); return n1 - n2; } }; /** The parent instance of solutions that will be hashed */ private final TRSPInstance mInstance; /** The random 32bits ints used for hashing (<code>Y<sub>i</sub></code>) */ final int[] mRndInts; /** * Creates a new <code>GroerSolutionHasher</code> * * @param instance */ public GroerSolutionHasher(TRSPInstance instance, RandomStream rndStream) { mInstance = instance; // Generate the random ints int size = mInstance.getDepotCount() + mInstance.getRequestCount(); mRndInts = new int[size]; rndStream.nextArrayOfInt(0, Integer.MAX_VALUE, mRndInts, 0, size); } /* (non-Javadoc) * @see vroom.trsp.datamodel.ISolutionHasher#hash(vroom.trsp.datamodel.TRSPSolution) */ @Override public int hash(TRSPSolution solution) { if (solution.getInstance() != mInstance) throw new IllegalArgumentException( "Solution parent instance is different from the reference instance"); int hash = 0; if (solution.getInstance().isCVRPTW()) { // Sort the tours to break symmetries ArrayList<TRSPTour> tours = new ArrayList<>(); for (int t = 0; t < solution.getTourCount(); t++) { tours.add(solution.getTour(t)); } Collections.sort(tours, TOUR_COMPARATOR); for (TRSPTour t : tours) { hash = hashTour(t, hash); } } else { for (int t = 0; t < solution.getTourCount(); t++) { hash = hashTour(solution.getTour(t), hash); } } return hash; } /** * Hashing of a tour * * @param tour * the tour to be hashed * @return a hash for the given tour */ @Override public int hash(ITRSPTour tour) { int hash = mRndInts[tour.getTechnicianId() % mRndInts.length]; return hashTour(tour, hash); } /** * Hashing of a tour from a start value * * @param tour * the tour to be hashed * @param hash * the initial hash value * @return the hash of the tour depending on the initial hash value */ int hashTour(ITRSPTour tour, int hash) { boolean skipEnds = tour.getSolution() != null && tour.getSolution().getInstance().isCVRPTW(); if (tour.length() == 0 || (skipEnds && tour.length() < 3)) return hash; ITourIterator it = tour.iterator(); if (skipEnds) it.next(); int prev = it.next(); while (it.hasNext() && (!skipEnds || prev != tour.getLastNode())) { int r = it.next(); hash ^= mRndInts[(r + prev) % mRndInts.length]; prev = r; } return hash; } }