From ac5d1b076ee6fba0272dcce5eab035617446f9a4 Mon Sep 17 00:00:00 2001 From: oblonski <4sschroeder@gmail.com> Date: Wed, 19 Nov 2014 07:24:53 +0100 Subject: [PATCH] add regret strat --- .../algorithm/recreate/RegretInsertion.java | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 jsprit-core/src/main/java/jsprit/core/algorithm/recreate/RegretInsertion.java diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/RegretInsertion.java b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/RegretInsertion.java new file mode 100644 index 00000000..008b8784 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/RegretInsertion.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (C) 2014 Stefan Schroeder + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + ******************************************************************************/ + +package jsprit.core.algorithm.recreate; + +import jsprit.core.problem.job.Job; +import jsprit.core.problem.job.Service; + +/** +* Insertion based on regret approach. +* +*

Basically calculates the insertion cost of the firstBest and the secondBest alternative. The score is then calculated as difference +* between secondBest and firstBest, plus additional scoring variables that can defined in this.ScoringFunction. +* The idea is that if the cost of the secondBest alternative is way higher than the first best, it seems to be important to insert this +* customer immediatedly. If difference is not that high, it might not impact solution if this customer is inserted later. +* +* @author stefan schroeder +* +*/ +public class RegretInsertion implements InsertionStrategy{ + + + /** + * Scorer to include other impacts on score such as time-window length or distance to depot. + * + * @author schroeder + * + */ + static interface ScoringFunction { + + public double score(Job job); + + } + + /** + * Scorer that includes the length of the time-window when scoring a job. The wider the time-window, the lower the score. + * + *

This is the default scorer, i.e.: score = (secondBest - firstBest) + this.TimeWindowScorer.score(job) + * + * @author schroeder + * + */ + static class TimeWindowScorer implements ScoringFunction { + + private double tw_scoringParam = - 0.1; + + @Override + public double score(Job job) { + double twStart = 0.0; + double twEnd = 0.0; +// if(job instanceof Shipment){ +// twStart = ((Shipment) job).getDeliveryTW().getStart(); +// twEnd = ((Shipment) job).getDeliveryTW().getEnd(); +// } +// else + if(job instanceof Service){ + twStart = ((Service) job).getTimeWindow().getStart(); + twEnd = ((Service) job).getTimeWindow().getEnd(); + } + return (twEnd-twStart)*tw_scoringParam; + } + + @Override + public String toString() { + return "[name=timeWindowScorer][scoringParam="+tw_scoringParam+"]"; + } + + } + + public static RegretInsertion newInstance(RouteAlgorithm routeAlgorithm) { + return new RegretInsertion(routeAlgorithm); + } + + private Logger logger = Logger.getLogger(RegretInsertion.class); + + private RouteAlgorithm routeAlgorithm; + + private ScoringFunction scoringFunction = new TimeWindowScorer(); + + /** + * Sets the scoring function. + * + *

By default, the this.TimeWindowScorer is used. + * + * @param scoringFunction + */ + public void setScoringFunction(ScoringFunction scoringFunction) { + this.scoringFunction = scoringFunction; + } + + public RegretInsertion(RouteAlgorithm routeAlgorithm) { + super(); + this.routeAlgorithm = routeAlgorithm; + logger.info("initialise " + this); + } + + @Override + public String toString() { + return "[name=regretInsertion][additionalScorer="+scoringFunction+"]"; + } + + public RouteAlgorithm getRouteAlgorithm(){ + return routeAlgorithm; + } + + /** + * Runs insertion. + * + *

Before inserting a job, all unassigned jobs are scored according to its best- and secondBest-insertion plus additional scoring variables. + * + */ + @Override + public void insertJobs(Collection routes, Collection unassignedJobs) { + List jobs = new ArrayList(unassignedJobs); +// informInsertionStarts(routes,unassignedJobs); + int inserted = 0; + while(!jobs.isEmpty()){ + List unassignedJobList = new ArrayList(jobs); + ScoredJob bestScoredJob = null; + double bestScore = -1*Double.MAX_VALUE; + VehicleRoute insertIn = null; + + for(Job unassignedJob : unassignedJobList){ + InsertionData best = null; + InsertionData secondBest = null; + VehicleRoute bestRoute = null; + + double benchmark = Double.MAX_VALUE; + for(VehicleRoute route : routes){ + if(secondBest != null){ + benchmark = secondBest.getInsertionCost(); + } + InsertionData iData = routeAlgorithm.calculateBestInsertion(route, unassignedJob, benchmark); + if(iData instanceof NoInsertionFound) continue; + if(best == null){ + best = iData; + bestRoute = route; + } + else if(iData.getInsertionCost() < best.getInsertionCost()){ + secondBest = best; + best = iData; + bestRoute = route; + } + else if(secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())){ + secondBest = iData; + } + } + if(best == null){ + break; + } + double score = score(unassignedJob,best,secondBest); + if(score > bestScore){ + bestScoredJob = new ScoredJob(unassignedJob,score,best,bestRoute); + bestScore = score; + } + } + Job assignedJob; + if(bestScoredJob == null){ + Job job = unassignedJobList.get(0); + VehicleRoute newRoute = VehicleRoute.emptyRoute(); + InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, job, Double.MAX_VALUE); + if(bestI instanceof InsertionData.NoInsertionFound) throw new IllegalStateException("given the vehicles, could not create a valid solution"); + insertIn=newRoute; + assignedJob=job; + routeAlgorithm.insertJob(job,bestI,newRoute); + routes.add(newRoute); + jobs.remove(job); + + } + else{ + routeAlgorithm.insertJob(bestScoredJob.getJob(),bestScoredJob.getInsertionData(), bestScoredJob.getRoute()); + insertIn=bestScoredJob.getRoute(); + assignedJob=bestScoredJob.getJob(); + jobs.remove(bestScoredJob.getJob()); + } + inserted++; +// informJobInserted(assignedJob, insertIn); + + } + } + + private double score(Job unassignedJob, InsertionData best, InsertionData secondBest) { + if(best == null){ + throw new IllegalStateException("cannot insert job " + unassignedJob.getId()); + } + if(secondBest == null){ + return Double.MAX_VALUE; + } + return (secondBest.getInsertionCost()-best.getInsertionCost()) + scoringFunction.score(unassignedJob); + + } + + @Override + public void removeListener(InsertionListener insertionListener) { + // TODO Auto-generated method stub + + } + + @Override + public Collection getListeners() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void addListener(InsertionListener insertionListener) { + // TODO Auto-generated method stub + + } + +} + +}