wip(marathon): algo

This commit is contained in:
Paul Alnet 2024-05-24 15:48:18 +02:00
parent 386a227978
commit ae73970aff

View file

@ -0,0 +1,193 @@
package org.insa.graphs.algorithm.marathon;
import java.util.ArrayList;
import java.util.Collections;
import org.insa.graphs.algorithm.AbstractAlgorithm;
import org.insa.graphs.algorithm.AbstractSolution.Status;
import org.insa.graphs.algorithm.shortestpath.Label;
import org.insa.graphs.algorithm.shortestpath.ShortestPathData;
import org.insa.graphs.algorithm.shortestpath.ShortestPathObserver;
import org.insa.graphs.algorithm.shortestpath.ShortestPathSolution;
import org.insa.graphs.algorithm.utils.BinaryHeap;
import org.insa.graphs.model.Arc;
import org.insa.graphs.model.Graph;
import org.insa.graphs.model.Node;
import org.insa.graphs.model.Path;
public class MarathonAlgorithm extends AbstractAlgorithm<ShortestPathObserver> {
protected double pathCost;
protected MarathonAlgorithm(ShortestPathData data) {
super(data);
}
@Override
public ShortestPathSolution run() {
return (ShortestPathSolution) super.run();
}
@Override
protected ShortestPathSolution doRun() {
final ShortestPathData data = getInputData();
ShortestPathSolution solution = null;
final Graph graph = data.getGraph();
final int nbNodes = graph.size();
// List of sommets, but with type Label
ArrayList<Label> labels = new ArrayList<Label>(nbNodes);
// Heap of sommets
BinaryHeap<Label> tas = new BinaryHeap<Label>();
// Notify observers about the first event (origin processed).
notifyOriginProcessed(data.getOrigin());
// Init Dijkstra
for (Node node : graph.getNodes()) {
// Luckily they are ordered by id.
// ArrayList.set only works if the value is already initialized (because why Java?)
labels.add(this.createLabel(node));
}
Label s = labels.get(data.getOrigin().getId());
// Add origin in the heap
// Label s = origin;//labels.get(0);
s.setPathCost(0);
tas.insert(s);
// On considère le sommet x à chaque itération
Label x = s;
final int dest_id = data.getDestination().getId();
while (!tas.isEmpty() && !(labels.get(dest_id).getNode().equals(x.getNode()))) {
x = tas.deleteMin();
x.mark();
notifyNodeMarked(x.getNode());
// System.out.println(x.getCost()); // Pour vérifier une croissance des noeuds marqués
// We create a list of node successors of x, instead of a list of Arcs.
double arc_cost = 0;
for (Arc successorArc : x.getNode().getSuccessors()) {
Label successor = labels.get(successorArc.getDestination().getId());
arc_cost = Double.MAX_VALUE;
if (!successor.isMarked() && data.isAllowed(successorArc)) {
// This loop serves to get the length of the arc as
// we know its origin and destination
for (Arc arc : x.getNode().getSuccessors()) {
if (successor.getNode().equals(arc.getDestination()) && data.getCost(arc) == data.getCost(successorArc)) {
// data.getcost(arc) returns a cost considering the mode chosen:
// TIME or LENGTH
// Similar to using getLength / getMinimumTravelTime
arc_cost = Math.min(data.getCost(arc), arc_cost);
}
}
final double possible_path_cost = x.getCost() + arc_cost;
if (successor.getCost() > possible_path_cost) {
// Mise à jour du label
successor.setPathCost(possible_path_cost);
successor.setParentArc(successorArc);
// Si le noeud n'a pas déjà été rajouté au tas, on le rajoute
// isReached permet de vérifier en complexité O(1)
// C'est un léger coût en mémoire pour un gain en vitesse
if (successor.isReached()) {
// removing then inserting resorts the binary heap
tas.remove(successor);
} else {
successor.markReached();
notifyNodeReached(successor.getNode());
}
tas.insert(successor);
}
}
}
}
if (labels.get(data.getDestination().getId()).getParentArc() == null) {
this.pathCost = 0;
solution = new ShortestPathSolution(data, Status.INFEASIBLE);
}
else {
// Create the path ...
ArrayList<Arc> arcs_path = new ArrayList<>();
Arc arc = labels.get(data.getDestination().getId()).getParentArc();
// We will find the path using the parent nodes, from the destination to the
// origin
while (arc != null && arc.getDestination().getId() != data.getOrigin().getId()) {
arcs_path.add(arc);
arc = labels.get(arc.getOrigin().getId()).getParentArc();
}
notifyDestinationReached(data.getDestination());
// Reverse the path...
Collections.reverse(arcs_path);
// Create the final solution.
solution = new ShortestPathSolution(data, Status.OPTIMAL, new Path(graph, arcs_path));
this.pathCost = labels.get(data.getDestination().getId()).getCost();
}
return solution;
}
@Override
public ShortestPathData getInputData() {
return (ShortestPathData) super.getInputData();
}
/**
* Notify all observers that the origin has been processed.
*
* @param node Origin.
*/
public void notifyOriginProcessed(Node node) {
for (ShortestPathObserver obs: getObservers()) {
obs.notifyOriginProcessed(node);
}
}
/**
* Notify all observers that a node has been reached for the first time.
*
* @param node Node that has been reached.
*/
public void notifyNodeReached(Node node) {
for (ShortestPathObserver obs: getObservers()) {
obs.notifyNodeReached(node);
}
}
/**
* Notify all observers that a node has been marked, i.e. its final value has
* been set.
*
* @param node Node that has been marked.
*/
public void notifyNodeMarked(Node node) {
for (ShortestPathObserver obs: getObservers()) {
obs.notifyNodeMarked(node);
}
}
/**
* Notify all observers that the destination has been reached.
*
* @param node Destination.
*/
public void notifyDestinationReached(Node node) {
for (ShortestPathObserver obs: getObservers()) {
obs.notifyDestinationReached(node);
}
}
public double getCostPath() {
return this.pathCost;
}
}