├── target ├── maven-status │ └── maven-compiler-plugin │ │ └── testCompile │ │ └── default-testCompile │ │ ├── createdFiles.lst │ │ └── inputFiles.lst └── maven-archiver │ └── pom.properties ├── .gitignore ├── src ├── main │ └── java │ │ └── com │ │ └── vrp │ │ └── app │ │ ├── Solver.java │ │ ├── components │ │ ├── Arc.java │ │ ├── Solution.java │ │ ├── Node.java │ │ ├── Route.java │ │ └── RelocationMove.java │ │ ├── utils │ │ └── Printer.java │ │ ├── solvers │ │ ├── NearestNeighbor.java │ │ ├── LocalSearchIntraRelocation.java │ │ ├── LocalSearchIntraAndInterRelocation.java │ │ └── TabuSearch.java │ │ └── VRP.java └── test │ └── java │ └── com │ └── vrp │ └── app │ └── VRPTest.java ├── Makefile ├── .github └── workflows │ └── maven.yml ├── README.md └── pom.xml /target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst: -------------------------------------------------------------------------------- 1 | com/vrp/app/VRPTest.class 2 | -------------------------------------------------------------------------------- /target/maven-archiver/pom.properties: -------------------------------------------------------------------------------- 1 | #Created by Apache Maven 3.6.3 2 | version=1.0-SNAPSHOT 3 | groupId=com.vrp.app 4 | artifactId=vrp 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.iml 3 | .project 4 | .classpath 5 | .o 6 | .log 7 | .idea/* 8 | *.lst 9 | *.jar 10 | *.txt 11 | *.xml 12 | *.lst 13 | 14 | -------------------------------------------------------------------------------- /target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst: -------------------------------------------------------------------------------- 1 | /home/michalis/rvs_new/vrp/src/test/java/com/vrp/app/VRPTest.java 2 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/Solver.java: -------------------------------------------------------------------------------- 1 | package com.vrp.app; 2 | 3 | import com.vrp.app.components.Solution; 4 | 5 | public interface Solver { 6 | void run(); 7 | 8 | void setSolution(Solution solution); 9 | 10 | Solution getSolution(); 11 | } 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | mvn -T1.5C clean package -DskipTests 5 | 6 | run: 7 | java -cp target/vrp-1.0-SNAPSHOT.jar com.vrp.app.VRP 1 30 10 8 | 9 | run-all: 10 | java -cp target/vrp-1.0-SNAPSHOT.jar com.vrp.app.VRP 1 30 10 11 | java -cp target/vrp-1.0-SNAPSHOT.jar com.vrp.app.VRP 2 30 10 12 | java -cp target/vrp-1.0-SNAPSHOT.jar com.vrp.app.VRP 3 30 10 13 | java -cp target/vrp-1.0-SNAPSHOT.jar com.vrp.app.VRP 4 30 10 14 | 15 | test: 16 | mvn test 17 | 18 | offline: 19 | mvn -T1.5C -o clean package 20 | 21 | clean: 22 | mvn clean 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 1.8 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 1.8 23 | - name: Build with Maven 24 | run: mvn -B clean package --file pom.xml 25 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/components/Arc.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.components; 25 | 26 | public class Arc { 27 | private int n1; 28 | private int n2; 29 | 30 | public Arc(int a, int b) { 31 | this.n1 = a; 32 | this.n2 = b; 33 | } 34 | 35 | public int getN1() { 36 | return n1; 37 | } 38 | 39 | public void setN1(int n1) { 40 | this.n1 = n1; 41 | } 42 | 43 | public int getN2() { 44 | return n2; 45 | } 46 | 47 | public void setN2(int n2) { 48 | this.n2 = n2; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/vrp/app/VRPTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app; 25 | 26 | import static org.junit.Assert.assertTrue; 27 | 28 | import org.junit.Test; 29 | 30 | /** 31 | * Unit test for simple App. 32 | */ 33 | public class VRPTest { 34 | /** 35 | * Rigorous Test :-) 36 | */ 37 | @Test 38 | public void shouldAnswerWithTrue() { 39 | assertTrue(true); 40 | } 41 | 42 | @Test 43 | public void shouldAnswerWithTrue1() { 44 | assertTrue(true); 45 | } 46 | 47 | @Test 48 | public void shouldAnswerWithTrue2() { 49 | assertTrue(true); 50 | } 51 | 52 | @Test 53 | public void shouldAnswerWithTrue3() { 54 | assertTrue(true); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/components/Solution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.components; 25 | 26 | import java.util.ArrayList; 27 | 28 | public class Solution { 29 | private double cost; 30 | private ArrayList route; 31 | 32 | public Solution() { 33 | this.route = new ArrayList<>(); 34 | this.cost = 0; 35 | } 36 | 37 | @SuppressWarnings("unchecked") 38 | public static Solution cloneSolution(Solution solution) { 39 | Solution out = new Solution(); 40 | out.setCost(solution.getCost()); 41 | out.setRoute((ArrayList) solution.getRoute().clone()); 42 | return out; 43 | } 44 | 45 | public double getCost() { 46 | return cost; 47 | } 48 | 49 | public void setCost(double costs) { 50 | this.cost = costs; 51 | } 52 | 53 | public void setRoute(ArrayList route) { 54 | this.route = route; 55 | } 56 | 57 | public ArrayList getRoute() { 58 | return route; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/components/Node.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.components; 25 | 26 | public class Node { 27 | private int x; 28 | private int y; 29 | private int id; 30 | private int demand; 31 | 32 | boolean routable; 33 | 34 | public Node(int x, int y, int id, int demand) { 35 | this.x = x; 36 | this.y = y; 37 | this.id = id; 38 | this.demand = demand; 39 | } 40 | 41 | public Node(int x, int y, int id) { 42 | this.x = x; 43 | this.y = y; 44 | this.id = id; 45 | } 46 | 47 | public boolean getRouted() { 48 | return routable; 49 | } 50 | 51 | public void setRouted(boolean rt) { 52 | this.routable = rt; 53 | } 54 | 55 | public int getX() { 56 | return x; 57 | 58 | } 59 | 60 | public int getY() { 61 | return y; 62 | 63 | } 64 | 65 | public int getId() { 66 | return id; 67 | } 68 | 69 | public void setId(int index) { 70 | this.id = index; 71 | } 72 | 73 | public int getDemand() { 74 | return demand; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/components/Route.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.components; 25 | 26 | import java.util.ArrayList; 27 | 28 | public class Route { 29 | private ArrayList nodes; 30 | private double cost; 31 | private int ID; 32 | private int load; 33 | private int capacity; 34 | 35 | public Route() { 36 | this.cost = 0; 37 | this.ID = -1; 38 | this.capacity = 50; 39 | this.load = 0; 40 | this.nodes = new ArrayList(); 41 | } 42 | 43 | public ArrayList getNodes() { 44 | return nodes; 45 | } 46 | 47 | public double getCost() { 48 | return cost; 49 | } 50 | 51 | public int getLoad() { 52 | return load; 53 | } 54 | 55 | public int getID() { 56 | return ID; 57 | } 58 | 59 | public int getCapacity() { 60 | return capacity; 61 | } 62 | 63 | public void setCost(double cost) { 64 | this.cost = cost; 65 | } 66 | 67 | public void setLoad(int load) { 68 | this.load = load; 69 | } 70 | 71 | public void setID(int idx) { 72 | this.ID = idx; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vehicle Routing Problem (VRP) Solvers 2 | [![HitCount](http://hits.dwyl.com/emarkou/Large-Scale-Optimization-Vehicle-Routing-Problem.svg)](http://hits.dwyl.com/emarkou/Large-Scale-Optimization-Vehicle-Routing-Problem) 3 | 4 | Java Constraint Solvers for Vehicle Routing Problem (VRP). 5 | 6 | # Quick Start 7 | ``` 8 | make && make run-all 9 | ``` 10 | 11 | # Features 12 | Currently the following algorithms are supported: 13 | - Nearest Neighbors 14 | - Local search with intra- relocation moves 15 | - Local search with intra- and inter- relocation moves 16 | - Tabu search 17 | 18 | 19 | #### Nearest Neigbors 20 | A constructive heuristic is developed based on Nearest Neighbor to produce an initial solution for the VRP. 21 | It is based on the nearest neighbor heuristic for TSP. 22 | However, in this case the capacity constraints of the routes must be taken into consideration while inserting a new customer. 23 | If no neighbor that respects the limitations exist, then the current route is finalized and the method continues by building the second route and so on. Obviously the method terminates when all customers have been inserted into the solution. 24 | 25 | #### Local Search with intra- relocations 26 | The nearest neighbors algorithm is initially applied. Afterwwards, the selected routes get refined using the local search method. 27 | This method considers all possible customer relocations within their routes. 28 | The relocation yielding the biggest cost reduction is selected to be applied to the candidate solution. 29 | The method terminates if no improving intra-route relocation can be identified. 30 | 31 | #### Local Search with intra- & inter- relocations 32 | This local search method is an extension of the one described above. 33 | This method considers all possible customer relocations (both intra- and inter-route). 34 | This means that at each iteration, the method explores all potential relocations of 35 | customers to any point of the existing solution. 36 | 37 | #### Tabu Search 38 | The nearest neighbors algorithm is initially applied. Afterwards, the selected routes get refined using a tabu-search method. 39 | This method considers all possible customer relocations (both intra- and inter-route) with respect to the selected tabu policy. 40 | The method terminates after 200 iterations. 41 | 42 | 43 | 44 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/utils/Printer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.utils; 25 | 26 | import com.vrp.app.components.Solution; 27 | 28 | public class Printer { 29 | public Printer(){ 30 | } 31 | 32 | public static void printResults(int numberOfVehicles, Solution solution, String solverName) { 33 | StringBuilder resultOutput = new StringBuilder(); 34 | resultOutput.append("* * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 35 | resultOutput.append("\t VRP Starts ! \n"); 36 | resultOutput.append(" Solver Used : " + solverName + " \n"); 37 | resultOutput.append("* * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 38 | 39 | for (int j = 0; j < numberOfVehicles; j++) { 40 | int vehicle = j + 1; 41 | resultOutput.append("Assignment to Vehicle " + vehicle + ": "); 42 | for (int k = 0; k < solution.getRoute().get(j).getNodes().size(); k++) { 43 | resultOutput.append(solution.getRoute().get(j).getNodes().get(k).getId() + " "); 44 | } 45 | resultOutput.append("\n"); 46 | resultOutput.append("Route Cost: " + solution.getRoute().get(j).getCost() + " - Route Load: " + solution.getRoute().get(j).getLoad() + "\n"); 47 | resultOutput.append("\n"); 48 | } 49 | resultOutput.append("* * * * * * * * * * * * * * * * * * * * * * * * * *\n"); 50 | resultOutput.append("Total Cost: " + solution.getCost() + "\n"); 51 | System.out.println(resultOutput); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.vrp.app 8 | vrp 9 | 1.0-SNAPSHOT 10 | 11 | vrp 12 | 13 | http://www.example.com 14 | 15 | 16 | UTF-8 17 | 1.7 18 | 1.7 19 | 20 | 21 | 22 | 23 | junit 24 | junit 25 | 4.13.1 26 | test 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | maven-clean-plugin 36 | 3.1.0 37 | 38 | 39 | 40 | maven-resources-plugin 41 | 3.0.2 42 | 43 | 44 | maven-compiler-plugin 45 | 3.8.0 46 | 47 | 48 | maven-surefire-plugin 49 | 2.22.1 50 | 51 | 52 | maven-jar-plugin 53 | 3.0.2 54 | 55 | 56 | maven-install-plugin 57 | 2.5.2 58 | 59 | 60 | maven-deploy-plugin 61 | 2.8.2 62 | 63 | 64 | 65 | maven-site-plugin 66 | 3.7.1 67 | 68 | 69 | maven-project-info-reports-plugin 70 | 3.0.0 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/components/RelocationMove.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.components; 25 | 26 | public class RelocationMove { 27 | private int positionOfRelocated; 28 | private int positionToBeInserted; 29 | private int route; 30 | private double moveCost; 31 | private int fromRoute; 32 | private int toRoute; 33 | private double moveCostTo; 34 | private double moveCostFrom; 35 | private int newLoadFrom; 36 | private int newLoadTo; 37 | 38 | public RelocationMove(int positionOfRelocated, int positionToBeInserted, int route, double moveCost) { 39 | this.positionOfRelocated = positionOfRelocated; 40 | this.positionToBeInserted = positionToBeInserted; 41 | this.route = route; 42 | this.moveCost = moveCost; 43 | } 44 | 45 | public RelocationMove(int positionOfRelocated, int positionToBeInserted, int fromRoute, int toRoute, double moveCostFrom, double moveCostTo) { 46 | this.positionOfRelocated = positionOfRelocated; 47 | this.positionToBeInserted = positionToBeInserted; 48 | this.fromRoute = fromRoute; 49 | this.toRoute = toRoute; 50 | this.moveCostFrom = moveCostFrom; 51 | this.moveCostTo = moveCostTo; 52 | } 53 | 54 | public int getPositionOfRelocated() { 55 | return positionOfRelocated; 56 | } 57 | 58 | public int getPositionToBeInserted() { 59 | return positionToBeInserted; 60 | } 61 | 62 | public int getRoute() { 63 | return route; 64 | } 65 | 66 | public double getMoveCost() { 67 | return moveCost; 68 | } 69 | 70 | public void setPositionOfRelocated(int positionOfRelocated) { 71 | this.positionOfRelocated = positionOfRelocated; 72 | } 73 | 74 | public void setPositionToBeInserted(int positionToBeInserted) { 75 | this.positionToBeInserted = positionToBeInserted; 76 | } 77 | 78 | public void setRoute(int route) { 79 | this.route = route; 80 | } 81 | 82 | public void setMoveCost(double moveCost) { 83 | this.moveCost = moveCost; 84 | } 85 | 86 | public int getFromRoute() { 87 | return fromRoute; 88 | } 89 | 90 | public void setFromRoute(int fromRoute) { 91 | this.fromRoute = fromRoute; 92 | } 93 | 94 | public int getToRoute() { 95 | return toRoute; 96 | } 97 | 98 | public void setToRoute(int toRoute) { 99 | this.toRoute = toRoute; 100 | } 101 | 102 | public double getMoveCostTo() { 103 | return moveCostTo; 104 | } 105 | 106 | public void setMoveCostTo(double moveCostTo) { 107 | this.moveCostTo = moveCostTo; 108 | } 109 | 110 | public double getMoveCostFrom() { 111 | return moveCostFrom; 112 | } 113 | 114 | public void setMoveCostFrom(double moveCostFrom) { 115 | this.moveCostFrom = moveCostFrom; 116 | } 117 | 118 | public int getNewLoadFrom() { 119 | return newLoadFrom; 120 | } 121 | 122 | public void setNewLoadFrom(int newLoadFrom) { 123 | this.newLoadFrom = newLoadFrom; 124 | } 125 | 126 | public int getNewLoadTo() { 127 | return newLoadTo; 128 | } 129 | 130 | public void setNewLoadTo(int newLoadTo) { 131 | this.newLoadTo = newLoadTo; 132 | } 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/solvers/NearestNeighbor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.solvers; 25 | 26 | import com.vrp.app.Solver; 27 | import com.vrp.app.components.Node; 28 | import com.vrp.app.components.Route; 29 | import com.vrp.app.components.Solution; 30 | 31 | import java.util.ArrayList; 32 | 33 | /** 34 | * A constructive heuristic is developed based on Nearest Neighbor to produce an initial solution for the VRP. 35 | * It is based on the nearest neighbor heuristic for TSP. However, in this case the capacity constraints of the routes 36 | * must be taken into consideration while inserting a new customer. If no neighbor that respects the limitations exist, 37 | * then the current route is finalized and the method continues by building the second route and so on. Obviously the method 38 | * terminates when all customers have been inserted into the solution. 39 | */ 40 | public class NearestNeighbor implements Solver { 41 | 42 | private final int numberOfVehicles; 43 | private final int numberOfCustomers; 44 | private final ArrayList allNodes; 45 | private final Node depot; 46 | private final double[][] distanceMatrix; 47 | private Solution solution; 48 | 49 | public NearestNeighbor(int numberOfCustomers, int numberOfVehicles, Node depot, double[][] distanceMatrix, ArrayList allNodes) { 50 | this.depot = depot; 51 | this.numberOfCustomers = numberOfCustomers; 52 | this.numberOfVehicles = numberOfVehicles; 53 | this.distanceMatrix = distanceMatrix; 54 | this.allNodes = allNodes; 55 | } 56 | 57 | @Override 58 | public void run() { 59 | Solution solution = new Solution(); 60 | ArrayList routes = solution.getRoute(); 61 | 62 | for (int i = 1; i <= numberOfVehicles; i++) { 63 | Route route_nodes = new Route(); 64 | route_nodes.setID(i); 65 | routes.add(route_nodes); 66 | } 67 | 68 | int toRoute = numberOfCustomers; 69 | for (int j = 1; j <= numberOfVehicles; j++) { 70 | ArrayList nodeSequence = routes.get(j - 1).getNodes(); 71 | int remaining = routes.get(j - 1).getCapacity(); 72 | int load = routes.get(j - 1).getLoad(); 73 | nodeSequence.add(depot); 74 | boolean finalized = false; 75 | if (toRoute == 0) { 76 | finalized = true; 77 | nodeSequence.add(depot); 78 | } 79 | 80 | while (finalized == false) { 81 | int positionOfTheNextOne = -1; 82 | double bestCostForTheNextOne = Double.MAX_VALUE; 83 | Node lastInTheRoute = nodeSequence.get(nodeSequence.size() - 1); 84 | for (int k = 1; k < allNodes.size(); k++) { 85 | Node candidate = allNodes.get(k); 86 | if (!candidate.getRouted()) { 87 | double trialCost = distanceMatrix[lastInTheRoute.getId()][candidate.getId()]; 88 | if (trialCost < bestCostForTheNextOne && candidate.getDemand() <= remaining) { 89 | positionOfTheNextOne = k; 90 | bestCostForTheNextOne = trialCost; 91 | } 92 | } 93 | } 94 | 95 | if (positionOfTheNextOne != -1) { 96 | Node insertedNode = allNodes.get(positionOfTheNextOne); 97 | nodeSequence.add(insertedNode); 98 | solution.setCost(solution.getCost() + bestCostForTheNextOne); 99 | routes.get(j - 1).setCost(routes.get(j - 1).getCost() + bestCostForTheNextOne); 100 | insertedNode.setRouted(true); 101 | remaining = remaining - insertedNode.getDemand(); 102 | load = load + insertedNode.getDemand(); 103 | routes.get(j - 1).setLoad(load); 104 | toRoute--; 105 | } else { 106 | nodeSequence.add(depot); 107 | solution.setCost(solution.getCost() + distanceMatrix[lastInTheRoute.getId()][0]); 108 | routes.get(j - 1).setCost(routes.get(j - 1).getCost() + distanceMatrix[lastInTheRoute.getId()][0]); 109 | finalized = true; 110 | } 111 | } 112 | } 113 | setSolution(solution); 114 | } 115 | 116 | @Override 117 | public void setSolution(Solution solution) { 118 | this.solution = solution; 119 | } 120 | 121 | @Override 122 | public Solution getSolution() { 123 | return solution; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/VRP.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app; 25 | 26 | import com.vrp.app.components.Node; 27 | import com.vrp.app.components.Solution; 28 | import com.vrp.app.solvers.LocalSearchIntraAndInterRelocation; 29 | import com.vrp.app.solvers.NearestNeighbor; 30 | import com.vrp.app.solvers.LocalSearchIntraRelocation; 31 | import com.vrp.app.solvers.TabuSearch; 32 | import com.vrp.app.utils.Printer; 33 | 34 | import java.util.ArrayList; 35 | import java.util.Random; 36 | 37 | public class VRP { 38 | public static final boolean DEBUG_ROUTES = false; 39 | private static int numberOfCustomers = 30; 40 | private static int numberOfVehicles = 10; 41 | private static int algorithm = 1; 42 | private static final boolean PRINT = true; 43 | 44 | public static void main(String[] args) { 45 | Solution finalSolution; 46 | String solverName = ""; 47 | 48 | setArgs(args); 49 | 50 | ArrayList allNodes = initCustomers(numberOfCustomers); 51 | Node depot = allNodes.get(0); 52 | 53 | double[][] distanceMatrix = initDistanceMatrix(allNodes); 54 | 55 | NearestNeighbor nearestNeighbor = new NearestNeighbor(numberOfCustomers, numberOfVehicles, depot, distanceMatrix, allNodes); 56 | nearestNeighbor.run(); 57 | 58 | switch (algorithm) { 59 | case 1: 60 | finalSolution = nearestNeighbor.getSolution(); 61 | solverName = "LocalSearch"; 62 | break; 63 | case 2: 64 | LocalSearchIntraRelocation localSearchIntraRelocation = new LocalSearchIntraRelocation(numberOfCustomers, numberOfVehicles, depot, distanceMatrix, allNodes); 65 | localSearchIntraRelocation.setSolution(nearestNeighbor.getSolution()); 66 | localSearchIntraRelocation.run(); 67 | finalSolution = localSearchIntraRelocation.getSolution(); 68 | solverName = "LocalSearch with Intra Relocation"; 69 | break; 70 | case 3: 71 | LocalSearchIntraAndInterRelocation localSearchIntraAndInterRelocation = new LocalSearchIntraAndInterRelocation(numberOfVehicles, numberOfCustomers, allNodes, depot, distanceMatrix); 72 | localSearchIntraAndInterRelocation.setSolution(nearestNeighbor.getSolution()); 73 | localSearchIntraAndInterRelocation.run(); 74 | finalSolution = localSearchIntraAndInterRelocation.getSolution(); 75 | solverName = "LocalSearch with Intra and Inner Relocation"; 76 | break; 77 | case 4: 78 | TabuSearch tabuSearch = new TabuSearch(numberOfVehicles, numberOfCustomers, allNodes, depot, distanceMatrix); 79 | tabuSearch.setSolution(nearestNeighbor.getSolution()); 80 | tabuSearch.run(); 81 | finalSolution = tabuSearch.getSolution(); 82 | solverName = "\t TABU Search "; 83 | break; 84 | default: 85 | throw new IllegalStateException("Unexpected value: " + algorithm); 86 | } 87 | 88 | 89 | if (PRINT) { 90 | Printer.printResults(numberOfVehicles, finalSolution, solverName); 91 | } 92 | } 93 | 94 | private static void setArgs(String[] args) { 95 | if (args.length != 0) { 96 | algorithm = Integer.parseInt(args[0]); 97 | numberOfCustomers = Integer.parseInt(args[1]); 98 | numberOfVehicles = Integer.parseInt(args[2]); 99 | } else { 100 | System.out.println("Arguments should be 3: 1) Algorithm 2) Number of Customers and 3) Number of Vehicles"); 101 | System.exit(1); 102 | } 103 | } 104 | 105 | 106 | private static double[][] initDistanceMatrix(ArrayList allNodes) { 107 | double[][] distanceMatrix = new double[allNodes.size()][allNodes.size()]; 108 | for (int i = 0; i < allNodes.size(); i++) { 109 | Node from = allNodes.get(i); 110 | 111 | for (int j = 0; j < allNodes.size(); j++) { 112 | Node to = allNodes.get(j); 113 | 114 | double Delta_x = (from.getX() - to.getX()); 115 | double Delta_y = (from.getY() - to.getY()); 116 | double distance = Math.sqrt((Delta_x * Delta_x) + (Delta_y * Delta_y)); 117 | 118 | distance = Math.round(distance); 119 | 120 | distanceMatrix[i][j] = distance; 121 | } 122 | } 123 | return distanceMatrix; 124 | } 125 | 126 | private static ArrayList initCustomers(int numberOfCustomers) { 127 | ArrayList allNodes = new ArrayList<>(); 128 | 129 | Node depot = new Node(50, 50, 0); 130 | depot.setRouted(true); 131 | allNodes.add(depot); 132 | 133 | Random ran = new Random(150589); 134 | 135 | for (int i = 1; i <= numberOfCustomers; i++) { 136 | int x = ran.nextInt(100); 137 | int y = ran.nextInt(100); 138 | int demand = 4 + ran.nextInt(7); 139 | Node customer = new Node(x, y, i, demand); 140 | customer.setRouted(false); 141 | allNodes.add(customer); 142 | } 143 | 144 | return allNodes; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/solvers/LocalSearchIntraRelocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.solvers; 25 | 26 | import com.vrp.app.Solver; 27 | import com.vrp.app.components.Node; 28 | import com.vrp.app.components.RelocationMove; 29 | import com.vrp.app.components.Solution; 30 | 31 | import java.util.ArrayList; 32 | 33 | /** 34 | * A local search is then designed for improving the initial solution generated at Component 3. 35 | * This local search method considers all possible customer relocations within their routes. 36 | * The relocation yielding the biggest cost reduction is selected to be applied to the candidate solution. 37 | * The method terminates if no improving intra-route relocation can be identified. 38 | */ 39 | public class LocalSearchIntraRelocation implements Solver { 40 | private final int numberOfVehicles; 41 | private final int numberOfCustomers; 42 | private final ArrayList allNodes; 43 | private final Node depot; 44 | private final double[][] distanceMatrix; 45 | private Solution solution; 46 | 47 | public LocalSearchIntraRelocation(int numberOfCustomers, int numberOfVehicles, Node depot, double[][] distanceMatrix, ArrayList allNodes) { 48 | this.depot = depot; 49 | this.numberOfCustomers = numberOfCustomers; 50 | this.numberOfVehicles = numberOfVehicles; 51 | this.distanceMatrix = distanceMatrix; 52 | this.allNodes = allNodes; 53 | } 54 | 55 | @Override 56 | public void run() { 57 | boolean terminationCondition = false; 58 | int localSearchIterator = 0; 59 | 60 | RelocationMove relocationMove = new RelocationMove(-1, -1, 0, Double.MAX_VALUE); 61 | 62 | while (!terminationCondition) { 63 | findBestRelocationMove(relocationMove, solution, distanceMatrix, numberOfVehicles); 64 | 65 | if (relocationMove.getMoveCost() < 0) { 66 | applyRelocationMove(relocationMove, solution, distanceMatrix); 67 | localSearchIterator = localSearchIterator + 1; 68 | 69 | } else { 70 | terminationCondition = true; 71 | } 72 | 73 | } 74 | } 75 | 76 | @Override 77 | public Solution getSolution() { 78 | return solution; 79 | } 80 | 81 | @Override 82 | public void setSolution(Solution solution) { 83 | this.solution = solution; 84 | } 85 | 86 | private void applyRelocationMove(RelocationMove relocationMove, Solution currentSolution, double[][] distanceMatrix) { 87 | Node relocatedNode = currentSolution.getRoute().get(relocationMove.getRoute()).getNodes().get(relocationMove.getPositionOfRelocated()); 88 | 89 | currentSolution.getRoute().get(relocationMove.getRoute()).getNodes().remove(relocationMove.getPositionOfRelocated()); 90 | 91 | if (relocationMove.getPositionToBeInserted() < relocationMove.getPositionOfRelocated()) { 92 | currentSolution.getRoute().get(relocationMove.getRoute()).getNodes().add(relocationMove.getPositionToBeInserted() + 1, relocatedNode); 93 | } else { 94 | currentSolution.getRoute().get(relocationMove.getRoute()).getNodes().add(relocationMove.getPositionToBeInserted(), relocatedNode); 95 | } 96 | 97 | double newSolutionCost = 0; 98 | 99 | for (int i = 0; i < currentSolution.getRoute().get(relocationMove.getRoute()).getNodes().size() - 1; i++) { 100 | Node A = currentSolution.getRoute().get(relocationMove.getRoute()).getNodes().get(i); 101 | Node B = currentSolution.getRoute().get(relocationMove.getRoute()).getNodes().get(i + 1); 102 | newSolutionCost = newSolutionCost + distanceMatrix[A.getId()][B.getId()]; 103 | } 104 | if (currentSolution.getRoute().get(relocationMove.getRoute()).getCost() + relocationMove.getMoveCost() != newSolutionCost) { 105 | System.out.println("Something went wrong with the cost calculations !!!!"); 106 | } 107 | 108 | currentSolution.setCost(currentSolution.getCost() + relocationMove.getMoveCost()); 109 | currentSolution.getRoute().get(relocationMove.getRoute()).setCost(currentSolution.getRoute().get(relocationMove.getRoute()).getCost() + relocationMove.getMoveCost()); 110 | 111 | setSolution(currentSolution); 112 | } 113 | 114 | private void findBestRelocationMove(RelocationMove relocationMove, Solution currentSolution, double[][] distanceMatrix, int numberOfVehicles) { 115 | double bestMoveCost = Double.MAX_VALUE; 116 | 117 | for (int j = 0; j < numberOfVehicles; j++) { 118 | for (int relIndex = 1; relIndex < currentSolution.getRoute().get(j).getNodes().size() - 1; relIndex++) { 119 | Node A = currentSolution.getRoute().get(j).getNodes().get(relIndex - 1); 120 | Node B = currentSolution.getRoute().get(j).getNodes().get(relIndex); 121 | Node C = currentSolution.getRoute().get(j).getNodes().get(relIndex + 1); 122 | 123 | for (int afterInd = 0; afterInd < currentSolution.getRoute().get(j).getNodes().size() - 1; afterInd++) { 124 | if (afterInd != relIndex && afterInd != relIndex - 1) { 125 | Node F = currentSolution.getRoute().get(j).getNodes().get(afterInd); 126 | Node G = currentSolution.getRoute().get(j).getNodes().get(afterInd + 1); 127 | 128 | double costRemoved1 = distanceMatrix[A.getId()][B.getId()] + distanceMatrix[B.getId()][C.getId()]; 129 | double costRemoved2 = distanceMatrix[F.getId()][G.getId()]; 130 | double costRemoved = costRemoved1 + costRemoved2; 131 | 132 | double costAdded1 = distanceMatrix[A.getId()][C.getId()]; 133 | double costAdded2 = distanceMatrix[F.getId()][B.getId()] + distanceMatrix[B.getId()][G.getId()]; 134 | double costAdded = costAdded1 + costAdded2; 135 | 136 | double moveCost = costAdded - costRemoved; 137 | 138 | if (moveCost < bestMoveCost) { 139 | bestMoveCost = moveCost; 140 | relocationMove.setPositionOfRelocated(relIndex); 141 | relocationMove.setPositionToBeInserted(afterInd); 142 | relocationMove.setMoveCost(moveCost); 143 | relocationMove.setRoute(j); 144 | } 145 | } 146 | } 147 | } 148 | } 149 | setSolution(currentSolution); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/solvers/LocalSearchIntraAndInterRelocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.solvers; 25 | 26 | import com.vrp.app.Solver; 27 | import com.vrp.app.VRP; 28 | import com.vrp.app.components.Node; 29 | import com.vrp.app.components.RelocationMove; 30 | import com.vrp.app.components.Solution; 31 | 32 | import java.util.ArrayList; 33 | 34 | /** 35 | * A local search is then designed for improving even further the initial solution generated at Component 3. 36 | * This local search method considers all possible customer relocations (both intra- and inter-route). 37 | * This means that at each iteration, the method explores all potential relocations of customers to any point of the existing solution. 38 | */ 39 | 40 | public class LocalSearchIntraAndInterRelocation implements Solver { 41 | private final int numberOfVehicles; 42 | private final int numberOfCustomers; 43 | private final ArrayList allNodes; 44 | private final Node depot; 45 | private final double[][] distanceMatrix; 46 | 47 | private Solution solution; 48 | 49 | public LocalSearchIntraAndInterRelocation(int numberOfVehicles, int numberOfCustomers, ArrayList allNodes, Node depot, double[][] distanceMatrix) { 50 | this.numberOfVehicles = numberOfVehicles; 51 | this.numberOfCustomers = numberOfCustomers; 52 | this.allNodes = allNodes; 53 | this.depot = depot; 54 | this.distanceMatrix = distanceMatrix; 55 | this.solution = new Solution(); 56 | } 57 | 58 | @Override 59 | public void run() { 60 | boolean terminationCondition = false; 61 | int localSearchIterator = 0; 62 | 63 | RelocationMove relocationMove = new RelocationMove(-1, -1, 0, 0, Double.MAX_VALUE, Double.MAX_VALUE); 64 | 65 | while (!terminationCondition) { 66 | findBestRelocationMove(relocationMove, solution, distanceMatrix, numberOfVehicles); 67 | 68 | if (relocationMove.getMoveCost() < 0) { 69 | applyRelocationMove(relocationMove, solution); 70 | localSearchIterator = localSearchIterator + 1; 71 | } else { 72 | terminationCondition = true; 73 | } 74 | } 75 | } 76 | 77 | private void applyRelocationMove(RelocationMove relocationMove, Solution currentSolution) { 78 | Node relocatedNode = currentSolution.getRoute().get(relocationMove.getFromRoute()).getNodes().get(relocationMove.getPositionOfRelocated()); 79 | 80 | currentSolution.getRoute().get(relocationMove.getFromRoute()).getNodes().remove(relocationMove.getPositionOfRelocated()); 81 | 82 | int whereToMove = (shouldShiftRelocation(relocationMove) ? 83 | relocationMove.getPositionToBeInserted() + 1 : relocationMove.getPositionToBeInserted()); 84 | 85 | currentSolution.getRoute().get(relocationMove.getToRoute()).getNodes().add(whereToMove, relocatedNode); 86 | 87 | currentSolution.setCost(currentSolution.getCost() + relocationMove.getMoveCost()); 88 | currentSolution.getRoute().get(relocationMove.getToRoute()).setCost(currentSolution.getRoute().get(relocationMove.getToRoute()).getCost() + relocationMove.getMoveCostTo()); 89 | currentSolution.getRoute().get(relocationMove.getFromRoute()).setCost(currentSolution.getRoute().get(relocationMove.getFromRoute()).getCost() + relocationMove.getMoveCostFrom()); 90 | if (relocationMove.getToRoute() != relocationMove.getFromRoute()) { 91 | currentSolution.getRoute().get(relocationMove.getToRoute()).setLoad(relocationMove.getNewLoadTo()); 92 | currentSolution.getRoute().get(relocationMove.getFromRoute()).setLoad(relocationMove.getNewLoadFrom()); 93 | } else { 94 | currentSolution.getRoute().get(relocationMove.getToRoute()).setLoad(relocationMove.getNewLoadTo()); 95 | } 96 | setSolution(currentSolution); 97 | } 98 | 99 | boolean shouldShiftRelocation(RelocationMove relocationMove) { 100 | return (relocationMove.getPositionToBeInserted() < relocationMove.getPositionOfRelocated()) || relocationMove.getToRoute() != relocationMove.getFromRoute(); 101 | } 102 | 103 | private void findBestRelocationMove(RelocationMove relocationMove, Solution currentSolution, double[][] distanceMatrix, int numberOfVehicles) { 104 | StringBuilder debug = new StringBuilder(); 105 | double bestMoveCost = Double.MAX_VALUE; 106 | 107 | for (int from = 0; from < numberOfVehicles; from++) { 108 | for (int to = 0; to < numberOfVehicles; to++) { 109 | for (int relFromIndex = 1; relFromIndex < currentSolution.getRoute().get(from).getNodes().size() - 1; relFromIndex++) { 110 | Node A = currentSolution.getRoute().get(from).getNodes().get(relFromIndex - 1); 111 | 112 | Node B = currentSolution.getRoute().get(from).getNodes().get(relFromIndex); 113 | 114 | Node C = currentSolution.getRoute().get(from).getNodes().get(relFromIndex + 1); 115 | 116 | for (int afterToInd = 0; afterToInd < currentSolution.getRoute().get(to).getNodes().size() - 1; afterToInd++) { 117 | if ((afterToInd != relFromIndex && afterToInd != relFromIndex - 1) || from != to) { 118 | Node F = currentSolution.getRoute().get(to).getNodes().get(afterToInd); 119 | 120 | Node G = currentSolution.getRoute().get(to).getNodes().get(afterToInd + 1); 121 | 122 | double costRemovedFrom = distanceMatrix[A.getId()][B.getId()] + distanceMatrix[B.getId()][C.getId()]; 123 | double costRemovedTo = distanceMatrix[F.getId()][G.getId()]; 124 | 125 | double costAddedFrom = distanceMatrix[A.getId()][C.getId()]; 126 | double costAddedTo = distanceMatrix[F.getId()][B.getId()] + distanceMatrix[B.getId()][G.getId()]; 127 | 128 | double moveCostFrom = costAddedFrom - costRemovedFrom; 129 | double moveCostTo = costAddedTo - costRemovedTo; 130 | 131 | double moveCost = moveCostFrom + moveCostTo; 132 | if ((moveCost < bestMoveCost) && (from == to || (currentSolution.getRoute().get(to).getLoad() + currentSolution.getRoute().get(from).getNodes().get(relFromIndex).getDemand() <= currentSolution.getRoute().get(to).getCapacity()))) { 133 | bestMoveCost = moveCost; 134 | 135 | relocationMove.setPositionOfRelocated(relFromIndex); 136 | relocationMove.setPositionToBeInserted(afterToInd); 137 | relocationMove.setMoveCostTo(moveCostTo); 138 | relocationMove.setMoveCostFrom(moveCostFrom); 139 | relocationMove.setFromRoute(from); 140 | relocationMove.setToRoute(to); 141 | relocationMove.setMoveCost(moveCost); 142 | 143 | if (from != to) { 144 | relocationMove.setNewLoadFrom(currentSolution.getRoute().get(from).getLoad() - currentSolution.getRoute().get(from).getNodes().get(relFromIndex).getDemand()); 145 | relocationMove.setNewLoadTo(currentSolution.getRoute().get(to).getLoad() + currentSolution.getRoute().get(from).getNodes().get(relFromIndex).getDemand()); 146 | } else { 147 | relocationMove.setNewLoadFrom(currentSolution.getRoute().get(from).getLoad()); 148 | relocationMove.setNewLoadTo(currentSolution.getRoute().get(to).getLoad()); 149 | } 150 | 151 | debug.append("From route: " + relocationMove.getFromRoute() + ", To Route: " + relocationMove.getToRoute() + ", New Load From:" + relocationMove.getNewLoadFrom() + ", New Load To:" + relocationMove.getNewLoadTo()); 152 | } 153 | } 154 | } 155 | } 156 | } 157 | } 158 | setSolution(currentSolution); 159 | if (VRP.DEBUG_ROUTES) { 160 | System.out.println(debug); 161 | } 162 | } 163 | 164 | @Override 165 | public void setSolution(Solution solution) { 166 | this.solution = solution; 167 | } 168 | 169 | @Override 170 | public Solution getSolution() { 171 | return solution; 172 | } 173 | 174 | public int getNumberOfVehicles() { 175 | return numberOfVehicles; 176 | } 177 | 178 | public int getNumberOfCustomers() { 179 | return numberOfCustomers; 180 | } 181 | 182 | public ArrayList getAllNodes() { 183 | return allNodes; 184 | } 185 | 186 | public Node getDepot() { 187 | return depot; 188 | } 189 | 190 | public double[][] getDistanceMatrix() { 191 | return distanceMatrix; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/main/java/com/vrp/app/solvers/TabuSearch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * */ 24 | package com.vrp.app.solvers; 25 | 26 | import com.vrp.app.Solver; 27 | import com.vrp.app.VRP; 28 | import com.vrp.app.components.Arc; 29 | import com.vrp.app.components.Node; 30 | import com.vrp.app.components.RelocationMove; 31 | import com.vrp.app.components.Solution; 32 | 33 | import java.util.ArrayList; 34 | import java.util.Random; 35 | 36 | /** 37 | * A tabu-search method is finally implemented for improving the initial solution generated by Nearest Neighbor algoirthm. 38 | * This tabu search method considers all possible customer relocations (both intra- and inter-route) with respect to the selected tabu policy. 39 | * The method terminates after 200 iterations. 40 | */ 41 | public class TabuSearch implements Solver { 42 | private final double TOLERANCE = 0.000001; 43 | private Random globalRandom = new Random(1); 44 | private final int numberOfVehicles; 45 | private final int numberOfCustomers; 46 | private final ArrayList allNodes; 47 | private final double[][] distanceMatrix; 48 | private Solution solution; 49 | private int[][] tabuArcs; 50 | private int TABU; 51 | 52 | public TabuSearch(int numberOfVehicles, int numberOfCustomers, ArrayList allNodes, Node depot, double[][] distanceMatrix) { 53 | this.numberOfVehicles = numberOfVehicles; 54 | this.numberOfCustomers = numberOfCustomers; 55 | this.allNodes = allNodes; 56 | this.distanceMatrix = distanceMatrix; 57 | this.solution = new Solution(); 58 | initTabuArcs(); 59 | } 60 | 61 | private void initTabuArcs() { 62 | tabuArcs = new int[allNodes.size()][allNodes.size()]; 63 | for (int i = 0; i < allNodes.size(); i++) { 64 | Node from = allNodes.get(i); 65 | for (int j = 0; j < allNodes.size(); j++) { 66 | Node to = allNodes.get(j); 67 | tabuArcs[from.getId()][to.getId()] = -1; 68 | } 69 | } 70 | } 71 | 72 | @Override 73 | public void run() { 74 | int tabuSearchIterator = 0; 75 | int MAX_ITERATIONS = 100; 76 | int bestSolutionIterator = 0; 77 | StringBuilder debug = new StringBuilder(); 78 | 79 | TABU = 18; 80 | 81 | Solution bestSolution = Solution.cloneSolution(solution); 82 | 83 | RelocationMove relocationMove = new RelocationMove(-1, -1, 0, 0, Double.MAX_VALUE, Double.MAX_VALUE); 84 | 85 | while (tabuSearchIterator < MAX_ITERATIONS) { 86 | tabuSearchIterator = tabuSearchIterator + 1; 87 | findBestRelocationMove(relocationMove, solution, distanceMatrix, tabuSearchIterator, bestSolution, numberOfVehicles); 88 | 89 | applyRelocationMove(relocationMove, solution, tabuSearchIterator); 90 | 91 | if (solution.getCost() < (bestSolution.getCost() - TOLERANCE)) { 92 | bestSolution = Solution.cloneSolution(solution); 93 | bestSolutionIterator = tabuSearchIterator; 94 | } 95 | 96 | debug.append("Iteration: " + tabuSearchIterator + " Best Cost: " + bestSolution.getCost() + " Current Cost: " + solution.getCost() + "\n"); 97 | 98 | if (tabuSearchIterator == 16 || tabuSearchIterator == 17) { 99 | debug.append("Iteration: " + tabuSearchIterator + "\n"); 100 | 101 | for (int j = 0; j < numberOfVehicles; j++) { 102 | int vehicle = j + 1; 103 | debug.append("New Assignment to Vehicle " + vehicle + ": "); 104 | for (int k = 0; k < solution.getRoute().get(j).getNodes().size(); k++) { 105 | debug.append(solution.getRoute().get(j).getNodes().get(k).getId() + " "); 106 | } 107 | debug.append("\n"); 108 | debug.append("Route Cost: " + solution.getRoute().get(j).getCost() + " - Route Load: " + solution.getRoute().get(j).getLoad() + "\n"); 109 | debug.append("\n"); 110 | } 111 | } 112 | } 113 | if (VRP.DEBUG_ROUTES) { 114 | debug.append("Best Solution Iteration: " + bestSolutionIterator + "\n"); 115 | System.out.println(debug); 116 | } 117 | } 118 | 119 | private void findBestRelocationMove(RelocationMove relocationMove, Solution currentSolution, double[][] distanceMatrix, int iteration, Solution bestSol, int numberOfVehicles) { 120 | Arc cr; 121 | ArrayList toBeCreated = new ArrayList<>(); 122 | 123 | double bestMoveCost = Double.MAX_VALUE; 124 | 125 | for (int from = 0; from < numberOfVehicles; from++) { 126 | for (int to = 0; to < numberOfVehicles; to++) { 127 | for (int relFromIndex = 1; relFromIndex < currentSolution.getRoute().get(from).getNodes().size() - 1; relFromIndex++) { 128 | 129 | Node A = currentSolution.getRoute().get(from).getNodes().get(relFromIndex - 1); 130 | Node B = currentSolution.getRoute().get(from).getNodes().get(relFromIndex); 131 | Node C = currentSolution.getRoute().get(from).getNodes().get(relFromIndex + 1); 132 | 133 | for (int afterToInd = 0; afterToInd < currentSolution.getRoute().get(to).getNodes().size() - 1; afterToInd++) { 134 | if (afterToInd != relFromIndex && afterToInd != relFromIndex - 1) { 135 | Node F = currentSolution.getRoute().get(to).getNodes().get(afterToInd); 136 | Node G = currentSolution.getRoute().get(to).getNodes().get(afterToInd + 1); 137 | 138 | double costRemovedFrom = distanceMatrix[A.getId()][B.getId()] + distanceMatrix[B.getId()][C.getId()]; 139 | double costRemovedTo = distanceMatrix[F.getId()][G.getId()]; 140 | 141 | double costAddedFrom = distanceMatrix[A.getId()][C.getId()]; 142 | double costAddedTo = distanceMatrix[F.getId()][B.getId()] + distanceMatrix[B.getId()][G.getId()]; 143 | 144 | double moveCostFrom = costAddedFrom - costRemovedFrom; 145 | double moveCostTo = costAddedTo - costRemovedTo; 146 | 147 | double moveCost = moveCostFrom + moveCostTo; 148 | 149 | toBeCreated.clear(); 150 | cr = new Arc(A.getId(), C.getId()); 151 | toBeCreated.add(cr); 152 | cr = new Arc(F.getId(), B.getId()); 153 | toBeCreated.add(cr); 154 | cr = new Arc(B.getId(), G.getId()); 155 | toBeCreated.add(cr); 156 | 157 | if (!isTabuArcs(toBeCreated, moveCost, currentSolution, iteration, bestSol)) { 158 | if ((moveCost < bestMoveCost) && (from == to || (currentSolution.getRoute().get(to).getLoad() + currentSolution.getRoute().get(from).getNodes().get(relFromIndex).getDemand() <= currentSolution.getRoute().get(to).getCapacity()))) { 159 | bestMoveCost = moveCost; 160 | 161 | relocationMove.setPositionOfRelocated(relFromIndex); 162 | relocationMove.setPositionToBeInserted(afterToInd); 163 | relocationMove.setMoveCostTo(moveCostTo); 164 | relocationMove.setMoveCostFrom(moveCostFrom); 165 | relocationMove.setFromRoute(from); 166 | relocationMove.setToRoute(to); 167 | relocationMove.setMoveCost(moveCost); 168 | 169 | if (from != to) { 170 | relocationMove.setNewLoadFrom(currentSolution.getRoute().get(from).getLoad() - currentSolution.getRoute().get(from).getNodes().get(relFromIndex).getDemand()); 171 | relocationMove.setNewLoadTo(currentSolution.getRoute().get(to).getLoad() + currentSolution.getRoute().get(from).getNodes().get(relFromIndex).getDemand()); 172 | } else { 173 | relocationMove.setNewLoadFrom(currentSolution.getRoute().get(from).getLoad()); 174 | relocationMove.setNewLoadTo(currentSolution.getRoute().get(to).getLoad()); 175 | } 176 | 177 | } 178 | } 179 | } 180 | } 181 | } 182 | } 183 | } 184 | } 185 | 186 | private void applyRelocationMove(RelocationMove relocationMove, Solution currentSolution, int iterations) { 187 | Node relocatedNode = currentSolution.getRoute().get(relocationMove.getFromRoute()).getNodes().get(relocationMove.getPositionOfRelocated()); 188 | 189 | Node A = currentSolution.getRoute().get(relocationMove.getFromRoute()).getNodes().get(relocationMove.getPositionOfRelocated() - 1); 190 | Node B = currentSolution.getRoute().get(relocationMove.getFromRoute()).getNodes().get(relocationMove.getPositionOfRelocated()); 191 | Node C = currentSolution.getRoute().get(relocationMove.getFromRoute()).getNodes().get(relocationMove.getPositionOfRelocated() + 1); 192 | Node F = currentSolution.getRoute().get(relocationMove.getToRoute()).getNodes().get(relocationMove.getPositionToBeInserted()); 193 | Node G = currentSolution.getRoute().get(relocationMove.getToRoute()).getNodes().get(relocationMove.getPositionToBeInserted() + 1); 194 | 195 | tabuArcs[A.getId()][B.getId()] = iterations + globalRandom.nextInt(TABU); 196 | tabuArcs[B.getId()][C.getId()] = iterations + globalRandom.nextInt(TABU); 197 | tabuArcs[F.getId()][G.getId()] = iterations + globalRandom.nextInt(TABU); 198 | 199 | currentSolution.getRoute().get(relocationMove.getFromRoute()).getNodes().remove(relocationMove.getPositionOfRelocated()); 200 | 201 | 202 | if ((relocationMove.getPositionToBeInserted() < relocationMove.getPositionOfRelocated()) || relocationMove.getToRoute() != relocationMove.getFromRoute()) { 203 | currentSolution.getRoute().get(relocationMove.getToRoute()).getNodes().add(relocationMove.getPositionToBeInserted() + 1, relocatedNode); 204 | } else { 205 | currentSolution.getRoute().get(relocationMove.getToRoute()).getNodes().add(relocationMove.getPositionToBeInserted(), relocatedNode); 206 | } 207 | 208 | currentSolution.setCost(currentSolution.getCost() + relocationMove.getMoveCost()); 209 | currentSolution.getRoute().get(relocationMove.getFromRoute()).setCost(currentSolution.getRoute().get(relocationMove.getFromRoute()).getCost() + relocationMove.getMoveCostFrom()); 210 | 211 | if (relocationMove.getToRoute() != relocationMove.getFromRoute()) { 212 | currentSolution.getRoute().get(relocationMove.getToRoute()).setLoad(relocationMove.getNewLoadTo()); 213 | currentSolution.getRoute().get(relocationMove.getFromRoute()).setLoad(relocationMove.getNewLoadFrom()); 214 | } else { 215 | currentSolution.getRoute().get(relocationMove.getToRoute()).setLoad(relocationMove.getNewLoadTo()); 216 | } 217 | 218 | setSolution(currentSolution); 219 | } 220 | 221 | private boolean isTabuArcs(ArrayList toBeCrt, double moveCost, Solution currentSolution, int iteration, Solution bestSolution) { 222 | if ((currentSolution.getCost() + moveCost) < (bestSolution.getCost() - TOLERANCE)) { 223 | return false; 224 | } 225 | 226 | for (int i = 0; i < toBeCrt.size(); i++) { 227 | Arc arc = toBeCrt.get(i); 228 | if (iteration < tabuArcs[arc.getN1()][arc.getN2()]) { 229 | return true; 230 | } 231 | } 232 | return false; 233 | } 234 | 235 | 236 | @Override 237 | public void setSolution(Solution solution) { 238 | this.solution = solution; 239 | } 240 | 241 | @Override 242 | public Solution getSolution() { 243 | return solution; 244 | } 245 | } 246 | --------------------------------------------------------------------------------