diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..19b9e6e914d5104afd0e03c031105f1c9708fac7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "cvrp_ls", + "request": "launch", + "mainClass": "cvrp_ls", + "projectName": "optialgos_92db6011", + "args": ["C:\\Users\\Jule Behrens\\Documents\\JonahAlgos\\optialgos\\src\\instances\\Loggi-n401-k23.vrp", "taboo_search", "3600", "50"] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 4869a8f9f5e76094828e08696aef53fb9c35dfb3..c78d38a05364e8295cc25f361b9a72d06c6b4ec9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Optimierungsalgorithmen für schwere Systeme WiSe 24/25 ### Author: Jonah Schierding ## Problem Set 1 Aufgabe 4 +## Problem Set 2 Aufgabe 1 @@ -10,4 +11,11 @@ Im src-Ordner liegen die Quelldateien und kompilierten Dateien. Aufruf erfolgt d (TYPE : CVRP, EDGE_WEIGHT_TYPE : EXPLICIT, EDGE_WEIGHT_FORMAT : LOWER_ROW) -zeigen muss. \ No newline at end of file +zeigen muss. + +Das Programm gibt die Kosten für die Initial ermittelte Lösung mit einem Greedy-Approach und die mit basic local search ohne Zeitlimit optimierte Lösung und anschließend die Lösungen selbst im .sol Format aus. + +Alternativ kann ein Algorithmus spezifiziert werden mit "java cvrp_ls *absolutePath* *algorithm*". Dabei sind die Optionen für *algorithm* "basic_local_serach", "taboo_search" und "ant_colony". +Für "taboo_search" und "ant_colony" muss zudem eine maximale Laufzeit für die Optimierung in vollen Sekunden angegeben werden ("java cvrp_ls *absolutePath* *algorithm* *maxTimeInSeconds*"). Für "basic_local_serach" ist das optional. + +"taboo_search" kann optional ein weiterer Parameter mitgegeben werden: Die Anzahl der Taboos. Der Standardwert liegt bei *Anzahl der Knoten/3* abgerundet. Ein möglicher Aufruf wäre: "java cvrp_ls *absolutePath* taboo_search *maxTimeInSeconds* *numberOfTaboos*". \ No newline at end of file diff --git a/bin/Algorithms.class b/bin/Algorithms.class index 8a02c8326f7828299319a119a016f9ad4dec6227..b6ae11b222029b818ae7893b44664a06c8973154 100644 Binary files a/bin/Algorithms.class and b/bin/Algorithms.class differ diff --git a/bin/Instance.class b/bin/Instance.class index 8845f4076d27d4ca304ae85e83cfcec3ebd35e7e..bafb11695094f5ed66efed0095c51e5103634fca 100644 Binary files a/bin/Instance.class and b/bin/Instance.class differ diff --git a/bin/Solution.class b/bin/Solution.class index d942cbee86cf073aaa077abb18516db858c50190..2cf9957a6dd60cf27a03de8ecc06e93c2f4377c3 100644 Binary files a/bin/Solution.class and b/bin/Solution.class differ diff --git a/bin/cvrp_ls.class b/bin/cvrp_ls.class index 4daff393d4b3346ed63729baf9cae5c3fe3ce26c..b881a47e3112097351bffd1753ab79d4097b2e25 100644 Binary files a/bin/cvrp_ls.class and b/bin/cvrp_ls.class differ diff --git a/src/Algorithms.class b/src/Algorithms.class index 9e57c751a76bf2586fe1ab06ef7e25d2b8d58588..fb297dc91aaf997c811fb976fbe8cf4dc18fc6f2 100644 Binary files a/src/Algorithms.class and b/src/Algorithms.class differ diff --git a/src/Algorithms.java b/src/Algorithms.java index 4e77456f90d47e2aa98e670b0f39d756b47c40ea..590b7d1d279c00ea4516a0b8b404490ee34f01a2 100644 --- a/src/Algorithms.java +++ b/src/Algorithms.java @@ -4,27 +4,142 @@ import java.util.Arrays; public class Algorithms { String algorithmType; - - public Algorithms(String algorithmType){ + int maxRuntimeInSeconds = -1; + int numberOfTaboos = -1; + public Algorithms(){} + public Algorithms(String algorithmType, String[] optionalParams) throws Exception{ this.algorithmType = algorithmType; - if(algorithmType == null || algorithmType.equals("")){ - this.algorithmType = "primitive_local_search"; + try { + this.maxRuntimeInSeconds = Integer.valueOf(optionalParams[0]); + } catch (Exception e) { + + } + //checks if algorithm specific parameters are feasible and assigns them + switch (algorithmType) { + case "none": + this.algorithmType = "basic_local_search"; + break; + case "ant_colony": + if(maxRuntimeInSeconds <= 0) + throw new Exception(); + break; + case "taboo_search": + try { + this.numberOfTaboos = Integer.valueOf(optionalParams[1]); + } catch (Exception e) { + + } + if(maxRuntimeInSeconds <= 0) + throw new Exception(); + if(this.numberOfTaboos <= 0) + System.out.println("Using standard value for number of Taboos." + ); + break; + default: + break; } } public Solution generateSolution(Instance instance){ - return generateInitialSolutionGreedy(instance); + switch (this.algorithmType) { + case "greedy": + return generateInitialSolutionGreedy(instance); + case "ant_colony": + return generateInitialSolutionAnt(instance); + default: + return null; + } } public Solution generateSolution(Solution solution){ switch (this.algorithmType) { - case "primitive_local_search": + case "basic_local_search": return localSearch(solution); + case "greedy": + return generateInitialSolutionGreedy(solution.instance); + case "taboo_search": + if(this.numberOfTaboos <= 0) + numberOfTaboos = solution.instance.getDimension()/3; + return tabooSearch(solution); + case "ant_colony": + return generateInitialSolutionAnt(solution.instance); default: return null; } } + private Solution tabooSearch(Solution solution) { + int[] state = {2,0,0,0}; + int newBestCount = 0; + + Solution currentSolution = solution; + Solution bestSolutionSeen = solution; + + long start = System.currentTimeMillis(); + long end = start + this.maxRuntimeInSeconds * 1000; + Solution possibleBetterNeighbor = getOtherNeighbor(state, bestSolutionSeen); + Solution currentBestNeighbor = possibleBetterNeighbor; + + int currentBestNeighborNodeChanged = 2; + + ArrayList<Integer> taboos = new ArrayList<>(); + + + while(maxRuntimeInSeconds > 0 && System.currentTimeMillis()<end){ + if(possibleBetterNeighbor.getCost() < currentBestNeighbor.getCost()){ + currentBestNeighbor = possibleBetterNeighbor; + currentBestNeighborNodeChanged = state[0]; + } + //permutate state to get another neighbor which is not taboo + state[0]++; + while(taboos.contains(state[0])){ + state[0]++; + } + if(state[0] > bestSolutionSeen.instance.getDimension()){ + //System.out.println("reset extracted node"); + state[0] = 2; + state[2]++; + if(state[2] > bestSolutionSeen.tours[state[1]].length-1){ + //System.out.println("reset position"); + state[2] = 0; + state[1]++; + if(state[1] > bestSolutionSeen.tours.length-1){ + //System.out.println("reset tours"); + state[1] = 0; + state[3]++; + //choose minimal after all neighbors are seen, update taboos, best solution see so far and current solution + if(state[3] == 2){ + currentSolution = currentBestNeighbor; + //System.out.println("Made a step, taboos: "+taboos.size()); + if(currentSolution.getCost() < bestSolutionSeen.getCost()){ + bestSolutionSeen = currentSolution; + + System.out.println("Found new best solution "+(++newBestCount) + ": All "+taboos.size()+" taboos dropped"); + taboos.clear(); + } else { + taboos.add(currentBestNeighborNodeChanged); + if(taboos.size() > numberOfTaboos){ + taboos.remove(0); + } + } + int[] help = {2,0,0,0}; + state = help; + currentBestNeighbor = getOtherNeighbor(state, currentSolution); + } + } + } + } + possibleBetterNeighbor = getOtherNeighbor(state, currentSolution); + } + System.out.println("terminated by time limit"); + return bestSolutionSeen; + } + + private Solution generateInitialSolutionAnt(Instance instance) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'generateInitialSolutionAnt'"); + } + //primitive greedy Approach private Solution generateInitialSolutionGreedy(Instance instance){ Solution solution = new Solution(instance); @@ -95,9 +210,17 @@ public class Algorithms { int[] state = {2,0,0,0}; Solution possibleBetterSolution = getOtherNeighbor(state, currentBestSolution); int newBestCount = 0; + long start = System.currentTimeMillis(); + long end = start + this.maxRuntimeInSeconds * 1000; //tries all neighbors as possibleBetterSolution, if better solution found, change currentBestSolution and search from there //terminates when it cannot find a better solution in its neighbarhood while (possibleBetterSolution != null) { + + //stop if max execution time has exceeded + if(maxRuntimeInSeconds > 0 && System.currentTimeMillis()>end){ + System.out.println("terminated by time limit"); + break; + } if(possibleBetterSolution.getCost() < currentBestSolution.getCost()){ currentBestSolution = possibleBetterSolution; int[] help = {2,0,0,0}; @@ -105,7 +228,6 @@ public class Algorithms { possibleBetterSolution = getOtherNeighbor(state, currentBestSolution); System.out.println("found new best solution "+(++newBestCount)); }else{ - //permutate state to get another neighbor state[0]++; if(state[0] > currentBestSolution.instance.getDimension()){ @@ -120,7 +242,9 @@ public class Algorithms { //System.out.println("reset tours"); state[1] = 0; state[3]++; + //stop if all neighbors are worse/local optimum is reached if(state[3] == 2){ + System.out.println("terminated by local optimum"); break; } } @@ -128,6 +252,7 @@ public class Algorithms { } possibleBetterSolution = getOtherNeighbor(state, currentBestSolution); } + } diff --git a/src/Instance.class b/src/Instance.class index a7624a488152f0e44889372417d520df2ea64979..e4611d38b6f7a38653ccd053cf29daf15aa610b4 100644 Binary files a/src/Instance.class and b/src/Instance.class differ diff --git a/src/Instance.java b/src/Instance.java index 784d9405faf1d4c51176d0d0c269ebba1b9f2b9c..4a1557cf0d4eb7b0b42f693cc91e5066eb734495 100644 --- a/src/Instance.java +++ b/src/Instance.java @@ -13,8 +13,9 @@ public class Instance { private Node depot; private int optimalCost; - public Instance(String fileLocation){ - //import instance of cvrp from file at fileLocation, lower Marix of distances + public Instance(){} + public Instance(String fileLocation) throws Exception{ + //import instance of cvrp from file at fileLocation and optimal solutioncost from sol file in same folder, lower Marix of distances try { File file = new File(fileLocation); Scanner scanner = new Scanner(file); @@ -67,20 +68,19 @@ public class Instance { } scanner.close(); } catch (FileNotFoundException e) { - System.out.println("An error occurred at import."); - e.printStackTrace(); + System.err.println("An error occurred at import, please check if your .vrp file fulfills the readme-conditions and the path is correct."); + throw new Exception(); } try { File file = new File(fileLocation.replace(".vrp", ".sol")); Scanner scanner = new Scanner(file); while (scanner.hasNextLine()) { String data = scanner.nextLine(); - if(data.contains("COST")) this.optimalCost = Integer.valueOf(data.replace("COST", "").trim()); + if(data.contains("Cost")) this.optimalCost = Integer.valueOf(data.replace("Cost", "").trim()); } scanner.close(); } catch (FileNotFoundException e) { - System.out.println("An error occurred at import."); - e.printStackTrace(); + System.out.println("No .sol file found for optimal solution."); } } diff --git a/src/Solution.java b/src/Solution.java index cdf582310c74dd3493ad3c0239cd444378e71fc0..9d53c54abdc73c92a53b168bf52e69ed906b9d1e 100644 --- a/src/Solution.java +++ b/src/Solution.java @@ -23,7 +23,6 @@ public class Solution { } lastNode = node; } catch (Exception e) { - // TODO: handle exception } @@ -55,4 +54,21 @@ public class Solution { copy.tours = toursCopy; return copy; } + + public String toSol(){ + String rep = ""; + for (int i = 0; i < tours.length; i++) { + rep += "Route #"+(i+1)+":"; + for (Node node : tours[i]) { + try { + rep += " "+node.number; + } catch (Exception e) { + + } + } + rep += "\n"; + } + rep += "Cost " + this.getCost(); + return rep; + } } \ No newline at end of file diff --git a/src/cvrp_ls.class b/src/cvrp_ls.class index aac62dc9d75652f282e88028984eb0c6e8dacd02..98308e98a6a7e534721ee16364b6e19e9714c6e5 100644 Binary files a/src/cvrp_ls.class and b/src/cvrp_ls.class differ diff --git a/src/cvrp_ls.java b/src/cvrp_ls.java index 0a12a27182cd6b2227bdf349f84ec5bc9c2069c9..481c19969c4bc5722126298caad645b160679fda 100644 --- a/src/cvrp_ls.java +++ b/src/cvrp_ls.java @@ -3,33 +3,54 @@ public class cvrp_ls { public static void main(String[] args) throws Exception { String fileLocation = ""; - String algorithm =""; - int maxRuntimeInSeconds = -1; + String algorithm ="none"; + String[] optionalParams = new String[args.length-2]; + System.arraycopy(args, 2, optionalParams, 0, args.length-2); + try { fileLocation = args[0]; algorithm = args[1]; - maxRuntimeInSeconds = Integer.valueOf(args[2]); - } catch (Exception e) { - + System.out.println("Something seems to be wrong with the given parameters"); + } //create new instance from file - Instance instance = new Instance(fileLocation); + Instance instance = new Instance(); + try { + instance = new Instance(fileLocation); + } catch (Exception e) { + System.err.println("Import of instance failed critically"); + e.printStackTrace(); + System.exit(1); + } + //create initial Greedy Solution - Algorithms greedy = new Algorithms("greedy"); + Algorithms greedy = new Algorithms("greedy", optionalParams); Solution greedySolution = greedy.generateSolution(instance); System.out.println("Greedy done"); //optimize with chosen algorithm - Algorithms chosen = new Algorithms(algorithm); + Algorithms chosen = new Algorithms(); + try { + chosen = new Algorithms(algorithm, optionalParams); + } catch (Exception e) { + System.err.println("Not all parameters for this algorithm seem to be right"); + } + Solution optimizedSolution = chosen.generateSolution(greedySolution); //print results - System.out.println("Instance name"+" & "+"Cost initial Solution"+" & "+"Cost optimized solution"+" & " + "Cost optimal solution"); - System.out.println(instance.getName()+" & "+greedySolution.getCost()+" & "+optimizedSolution.getCost()+" & "+instance.getOptimalCost()); + System.out.println(("\nAfter initial greedy Approach, optimized with "+chosen.algorithmType+" with a maximum runtime of approximately "+chosen.maxRuntimeInSeconds+" seconds!").replace("-1", "unlimited")); + System.out.println("Instance name"+" & "+"Cost initial Solution"+" & "+"Cost optimized solution ("+chosen.algorithmType+")"+" & " + "Cost optimal solution (100%)"+ " \\\\"); + if(instance.getOptimalCost() > 0) + System.out.println(instance.getName()+" & "+greedySolution.getCost()+" ("+(float)greedySolution.getCost()/(float)instance.getOptimalCost()*100+"%) & "+optimizedSolution.getCost()+" ("+(float)optimizedSolution.getCost()/(float)instance.getOptimalCost()*100+"%) & "+instance.getOptimalCost() + " \\\\"); + else + System.out.println(instance.getName()+" & "+greedySolution.getCost()+" & "+optimizedSolution.getCost()+" & "+ " \\\\"); + System.out.println("\n Initial solution:\n"+greedySolution.toSol()); + System.out.println("\n Optimized solution:\n"+optimizedSolution.toSol()); } }