/** * */ package vroom.trsp.optimization.rch; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import vroom.common.utilities.DoublyLinkedIntList; import vroom.common.utilities.DoublyLinkedIntSet; import vroom.trsp.datamodel.ITRSPTour; import vroom.trsp.datamodel.TRSPInstance; import vroom.trsp.datamodel.TRSPSimpleTour; import vroom.trsp.datamodel.Technician; import vroom.trsp.datamodel.costDelegates.TRSPCostDelegate; import vroom.trsp.optimization.constraints.TourConstraintHandler; import vroom.trsp.util.TRSPGlobalParameters; import vroom.trsp.util.TRSPLogging; /** * <code>RndNearestFurthestIns</code> is an implementation of both random nearest and farthest insertion. * <p> * Creation date: Oct 4, 2011 - 1:44:48 PM * * @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 RndNearestFurthestIns extends TRSPRndConstructiveHeuristic { private final boolean mRFI; private final NeighborComparator mComparator; private final Neighbor[] mNearestTourNode; private final DoublyLinkedIntSet mUnserved; // private final LimitedSortedList<Neighbor> mBestNeighbors; private final ArrayList<Neighbor> mBestNeighbors; /** * Creates a new <code>TRSPRndNearestFarthestInsOld</code> * * @param instance * @param parameters * @param constraintHandler * @param costDelegate * @param kMax * the K<sub>max</sub> value * @param rfi * <code>true</code> if the farthest insertion should be selected (RFI), <code>false</code> if the * nearest insertion should be selected (RNI) */ public RndNearestFurthestIns(TRSPInstance instance, TRSPGlobalParameters parameters, TourConstraintHandler constraintHandler, TRSPCostDelegate costDelegate, int kMax, boolean rfi) { super(instance, parameters, constraintHandler, costDelegate, kMax); mNearestTourNode = new Neighbor[getInstance().getMaxId()]; mUnserved = new DoublyLinkedIntSet(getInstance().getMaxId()); mRFI = rfi; mComparator = mRFI ? new InvNeighborComparator() : new NeighborComparator(); // mBestNeighbors = new LimitedSortedList<Neighbor>(getKmax(), mComparator); mBestNeighbors = new ArrayList<Neighbor>(mUnserved.size()); } @Override protected Collection<ITRSPTour> generateGiantTour(Technician technician) { int tech = technician.getID(); Arrays.fill(mNearestTourNode, null); mUnserved.clear(); // Initialize the nearest tour node array for (ObservableNode node : getCompatibleRequests(tech)) { double dist = getInstance().getCostDelegate().getDistance(getHome(tech).getId(), node.getId()); Neighbor neighbor = new Neighbor(node, getHome(tech), dist); mNearestTourNode[node.getId()] = neighbor; mBestNeighbors.add(neighbor); mUnserved.add(node.getId()); } Collections.sort(mBestNeighbors, mComparator); Collection<ITRSPTour> tours = new LinkedList<ITRSPTour>(); boolean abort = false; while (!mUnserved.isEmpty() && !abort) { abort = true; // Initialize the tour List<Integer> tour = initTour(tech); // Until all compatible requests have been visited while (!mUnserved.isEmpty()) { // ObservableNode node = selectNextNode(tour, tech, isUnserved, unserved); ObservableNode neigh = selectNextNode(tour, tech); if (neigh == null) break; insertNextNode(tour, neigh, tech); nodeInserted(neigh); abort = false; } tours.add(new TRSPSimpleTour(tech, getInstance(), tour)); } if (!mUnserved.isEmpty()) TRSPLogging.getOptimizationLogger().info( "RndNearestFurthestIns.generateGiantTour: %s request(s) left unserved: %s", mUnserved.size(), mUnserved); return tours; } @Override protected ITRSPTour generateFeasibleTour(Technician tech) { throw new UnsupportedOperationException("Not implemented yet"); } protected List<Integer> initTour(int tech) { DoublyLinkedIntList tour = new DoublyLinkedIntList(getInstance().getMaxId()); // Add the technician home tour.add(getInstance().getTechnician(tech).getHome().getID()); // TODO check if we really want to add a random node at the beginning // Add a random node // int rnd = getParameters().getRCHRndStream().nextInt(0, mBestNeighbors.size() - 1); // Neighbor seed = mBestNeighbors.remove(rnd); // tour.add(seed.getNode().getId()); // nodeInserted(seed.getNode()); // Return the list return tour; } protected ObservableNode selectNextNode(List<Integer> tour, int tech) { if (mBestNeighbors.isEmpty()) return null; else return mBestNeighbors.remove(nextIdx(mBestNeighbors.size())).getNode(); } protected void insertNextNode(List<Integer> ltour, ObservableNode node, int tech) { if (ltour.isEmpty()) ltour.add(node.getId()); double bestInsCost = Double.POSITIVE_INFINITY; Integer bestInsSucc = null; DoublyLinkedIntList tour = (DoublyLinkedIntList) ltour; Technician technician = getInstance().getTechnician(tech); // Check for all possible insertions ListIterator<Integer> it = tour.listIterator(); Integer pred = it.next(); while (it.hasNext()) { Integer succ = it.next(); double insCost = evaluateInsertionCost(node.getId(), pred, succ, technician, true); if (insCost < bestInsCost) { bestInsCost = insCost; bestInsSucc = succ; } pred = succ; } // Insertion in the last position before the return to the depot Integer succ = tour.getFirst(); double insCost = evaluateInsertionCost(node.getId(), pred, succ, technician, true); if (insCost < bestInsCost) { tour.add(node.getId()); } else { tour.insert(node.getId(), bestInsSucc); } } /** * Update the array of nearest tour node after a new node is inserted in the current tour * * @param node * the inserted node */ protected void nodeInserted(ObservableNode node) { mNearestTourNode[node.getId()] = null; mUnserved.remove(node.getId()); node.markAsServed(); boolean changed = false; for (Integer u : mUnserved) { double dist = getInstance().getCostDelegate().getDistance(node.getId(), u); if (mComparator.compare(dist, mNearestTourNode[u].getDistance()) < 0) { // mNearestTourNode[u] = new Neighbor(getObsNode(u), node, dist); mNearestTourNode[u].update(node, dist); // Maintain a list of the Kmax-best decisions // mBestNeighbors.add(mNearestTourNode[u]); changed = true; } } if (changed) Collections.sort(mBestNeighbors, mComparator); } @Override public String toString() { return mRFI ? "RFI" : "RNI"; } /** * <code>InvNeighborComparator</code> is used to sort neighbors in the RNI variant */ protected class NeighborComparator implements Comparator<Neighbor> { @Override public int compare(Neighbor o1, Neighbor o2) { return compare(o1.getDistance(), o2.getDistance()); } public int compare(double d1, double d2) { return Double.compare(d1, d2); } } /** * <code>InvNeighborComparator</code> is used to sort neighbors in the RFI variant */ protected class InvNeighborComparator extends NeighborComparator { @Override public int compare(double d1, double d2) { return -Double.compare(d1, d2); } } }