mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
add regret strat
This commit is contained in:
parent
06770c7e31
commit
ac5d1b076e
1 changed files with 227 additions and 0 deletions
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
package jsprit.core.algorithm.recreate;
|
||||
|
||||
import jsprit.core.problem.job.Job;
|
||||
import jsprit.core.problem.job.Service;
|
||||
|
||||
/**
|
||||
* Insertion based on regret approach.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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<VehicleRoute> routes, Collection<Job> unassignedJobs) {
|
||||
List<Job> jobs = new ArrayList<Job>(unassignedJobs);
|
||||
// informInsertionStarts(routes,unassignedJobs);
|
||||
int inserted = 0;
|
||||
while(!jobs.isEmpty()){
|
||||
List<Job> unassignedJobList = new ArrayList<Job>(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<InsertionListener> getListeners() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(InsertionListener insertionListener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue