Skip to content
GitLab
Projets
Groupes
Sujets
Extraits de code
/
Aide
Aide
Support
Forum de la communauté
Raccourcis clavier
?
Proposer une rétroaction
Contribuer à GitLab
Connexion
Activer/désactiver la navigation
Menu
MOLINIER Hugo
M1-IWOCS_Graph-TP3
Comparer les révisions
7a1b7a5e71da89d695b1e799f4bc38470d84587e...27c612f73669b1e0a18e431514ceca53681d4b57
Tout étendre
Masquer les modifications d'espaces
En ligne
Côte à côte
src/main/java/org/example/GraphManipulation.java
0 → 100644
Voir le fichier @
27c612f7
package
org.example
;
import
org.example.entity.BellmanFordResult
;
import
org.example.entity.PathResult
;
import
org.graphstream.graph.Edge
;
import
org.graphstream.graph.Graph
;
import
org.graphstream.graph.Node
;
import
org.graphstream.graph.implementations.SingleGraph
;
import
org.graphstream.stream.file.FileSource
;
import
org.graphstream.stream.file.FileSourceFactory
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.atomic.AtomicBoolean
;
/**
* Cette classe fournit des méthodes utilitaires pour manipuler des graphes représentant
* des villes et calculer les plus courts chemins pour un vélo électrique.
*/
public
class
GraphManipulation
{
/**
* Reconstruit le chemin le plus court jusqu'à un nœud de destination
* à partir des résultats de Bellman-Ford.
*
* @param bellmanFordResult Résultat de l'algorithme Bellman-Ford contenant les coûts et prédécesseurs.
* @param endNode Le nœud de destination du chemin à reconstruire.
* @return Un objet PathResult contenant la liste des nœuds du chemin (dans l'ordre source → destination)
* et le coût total pour atteindre endNode.
*/
public
static
PathResult
getPath
(
BellmanFordResult
bellmanFordResult
,
Node
endNode
){
Map
<
Node
,
Node
>
pred
=
bellmanFordResult
.
pred
();
List
<
Node
>
chemin
=
new
ArrayList
<>();
Node
current
=
endNode
;
while
(
current
!=
null
)
{
chemin
.
add
(
current
);
current
=
pred
.
get
(
current
);
}
return
new
PathResult
(
chemin
.
reversed
(),
bellmanFordResult
.
mapCost
().
get
(
endNode
));
}
/**
* Marque un chemin dans le graphe pour l'affichage.
* Les nœuds et les arêtes du chemin seront mis en évidence.
*
* @param chemin Liste des nœuds représentant le chemin (de la source à la destination).
*/
public
static
void
markPath
(
List
<
Node
>
chemin
)
{
for
(
int
i
=
0
;
i
<
chemin
.
size
();
i
++)
{
Node
node
=
chemin
.
get
(
i
);
if
(
i
<
chemin
.
size
()
-
1
)
{
Node
nextNode
=
chemin
.
get
(
i
+
1
);
Edge
edge
=
node
.
getEdgeBetween
(
nextNode
);
if
(
edge
!=
null
)
{
edge
.
setAttribute
(
"ui.class"
,
"inPath"
);
}
}
}
}
/**
* Calcule le coût en électricité pour parcourir une arête d'un graphe représentant
* une ville pour un vélo électrique.
* @param edge L'arête dont on souhaite calculer le coût.
* @return Le coût en Wh pour parcourir cette arête. Les descentes donnent un coût négatif.
*/
public
static
Float
getCostOfTheArete
(
Edge
edge
)
{
Integer
aHauteur
=
edge
.
getSourceNode
().
getAttribute
(
"hauteur"
,
Integer
.
class
);
Integer
bHauteur
=
edge
.
getTargetNode
().
getAttribute
(
"hauteur"
,
Integer
.
class
);
return
(
aHauteur
<=
bHauteur
)?
(
5
*(
bHauteur
-
aHauteur
)+
2
f
)
:
((
bHauteur
-
aHauteur
)/
2
f
);
}
/**
* Charge un graphe depuis un fichier .dgs et retourne l'objet Graph correspondant.
*
* @param path Chemin vers le fichier .dgs.
* @return L'objet Graph chargé depuis le fichier.
*/
public
static
Graph
getTheFileFromPath
(
String
path
)
{
Graph
graph
=
new
SingleGraph
(
"LoadedGraph"
);
try
{
FileSource
fs
=
FileSourceFactory
.
sourceFor
(
path
);
if
(
fs
==
null
)
{
throw
new
IOException
(
"Format de fichier non reconnu : "
+
path
);
}
fs
.
addSink
(
graph
);
fs
.
readAll
(
path
);
fs
.
removeSink
(
graph
);
}
catch
(
Exception
e
)
{
System
.
err
.
println
(
"Erreur lors du chargement du graphe : "
+
e
.
getMessage
());
return
graph
;
}
return
graph
;
}
/**
* Implémente l'algorithme de Bellman-Ford pour calculer le plus court chemin
* depuis un nœud source dans un graphe orienté avec des coûts qui peuvent être négatifs.
*
* @param graph Le graphe orienté sur lequel calculer les plus courts chemins.
* @param startNode Le nœud source depuis lequel on calcule les plus courts chemins.
* @return Un objet BellmanFordResult contenant les maps des coûts et des prédécesseurs.
* 1. mapCost : les coûts minimaux pour atteindre chaque nœud depuis startNode.
* 2. pred : les prédécesseurs de chaque nœud pour reconstruire le plus court chemin.
*/
public
static
BellmanFordResult
BellmanFord
(
Graph
graph
,
Node
startNode
){
Map
<
Node
,
Float
>
mapCost
=
new
HashMap
<>();
Map
<
Node
,
Node
>
pred
=
new
HashMap
<>();
graph
.
forEach
(
node
->
{
float
value
=
node
==
startNode
?
0
f:
Float
.
POSITIVE_INFINITY
;
mapCost
.
put
(
node
,
value
);
pred
.
put
(
node
,
null
);
});
AtomicBoolean
updated
=
new
AtomicBoolean
(
true
);
for
(
int
k
=
1
;
k
<
graph
.
getNodeCount
()
-
1
&&
updated
.
get
();
k
++)
{
updated
.
set
(
false
);
graph
.
edges
().
forEach
(
edge
->{
float
edgeCost
=
edge
.
hasAttribute
(
"weight"
)
?
((
Number
)
edge
.
getAttribute
(
"weight"
)).
floatValue
()
:
getCostOfTheArete
(
edge
);
Node
sourceNode
=
edge
.
getSourceNode
();
//u
Node
targetNode
=
edge
.
getTargetNode
();
//v
if
(
mapCost
.
get
(
sourceNode
)
+
edgeCost
<
mapCost
.
get
(
targetNode
))
{
mapCost
.
put
(
targetNode
,
mapCost
.
get
(
sourceNode
)
+
edgeCost
);
pred
.
put
(
targetNode
,
sourceNode
);
updated
.
set
(
true
);
}
});
}
return
new
BellmanFordResult
(
mapCost
,
pred
);
}
}
src/main/java/org/example/Main.java
0 → 100644
Voir le fichier @
27c612f7
package
org.example
;
import
org.example.entity.BellmanFordResult
;
import
org.example.entity.PathResult
;
import
org.graphstream.graph.Graph
;
import
org.graphstream.graph.Node
;
import
static
org
.
example
.
GraphManipulation
.*;
public
class
Main
{
public
static
void
main
(
String
[]
args
)
{
System
.
setProperty
(
"org.graphstream.ui"
,
"swing"
);
Graph
graph
=
getTheFileFromPath
(
"src/main/resources/graphe_lehavre.dgs"
);
if
(
graph
!=
null
){
Node
startNode
=
graph
.
getNode
(
"33317746"
);
Node
endNode
=
graph
.
getNode
(
"144477138"
);
BellmanFordResult
result
=
BellmanFord
(
graph
,
startNode
);
PathResult
pathResult
=
getPath
(
result
,
endNode
);
markPath
(
pathResult
.
path
());
System
.
out
.
println
(
"Cout du plus court chemin : "
+
pathResult
.
cost
());
System
.
out
.
println
(
"chemin trouvé : "
+
pathResult
.
path
());
graph
.
setAttribute
(
"ui.stylesheet"
,
"url(file://src/main/resources/style.css)"
);
graph
.
display
(
false
);
}
}
}
\ No newline at end of file
src/main/java/org/example/entity/BellmanFordResult.java
0 → 100644
Voir le fichier @
27c612f7
package
org.example.entity
;
import
org.graphstream.graph.Node
;
import
java.util.Map
;
public
record
BellmanFordResult
(
Map
<
Node
,
Float
>
mapCost
,
Map
<
Node
,
Node
>
pred
){}
src/main/java/org/example/entity/PathResult.java
0 → 100644
Voir le fichier @
27c612f7
package
org.example.entity
;
import
org.graphstream.graph.Node
;
import
java.util.List
;
public
record
PathResult
(
List
<
Node
>
path
,
Float
cost
)
{
}
src/main/resources/graphe_cherbourg.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/graphe_grenoble.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/graphe_isneauville.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/graphe_lehavre.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/graphe_montivilliers.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/graphe_morlaix.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/graphe_ploemer.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/graphe_rome.dgs
0 → 100644
Voir le fichier @
27c612f7
Ce diff est replié.
Cliquez pour l’agrandir.
src/main/resources/style.css
0 → 100644
Voir le fichier @
27c612f7
node
{
size
:
0px
;
}
edge
{
arrow-size
:
0px
,
0px
;
}
edge
.inPath
{
size
:
4
;
fill-color
:
red
;
}
src/test/java/org/example/MainTest.java
0 → 100644
Voir le fichier @
27c612f7
package
org.example
;
import
org.example.entity.BellmanFordResult
;
import
org.example.entity.PathResult
;
import
org.graphstream.algorithm.BellmanFord
;
import
org.graphstream.graph.Graph
;
import
org.graphstream.graph.Node
;
import
java.io.File
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.concurrent.*
;
import
static
org
.
example
.
GraphManipulation
.*;
import
static
org
.
example
.
Utils
.
getRandomNode
;
import
static
org
.
example
.
Utils
.
saveDat
;
/**
* Classe de test pour comparer les performances de l'algorithme Bellman-Ford
* implémenté manuellement avec celui de la bibliothèque GraphStream.
*/
public
class
MainTest
{
private
static
final
int
DEFAULT_TESTS_PER_GRAPHCOMPARATE
=
5
;
private
static
final
int
DEFAULT_TESTS_PER_GRAPH_MY_BELLMAN
=
25
;
private
static
final
int
DEFAULT_TIMEOUT_SECONDS
=
90
;
private
static
final
String
GNUPLOTPATH
=
"gnuplot/dat/"
;
private
static
final
String
GRAPHPATH
=
"src/main/resources/"
;
public
static
void
main
(
String
[]
args
)
{
System
.
setProperty
(
"org.graphstream.ui"
,
"swing"
);
File
[]
dgsFiles
=
new
File
(
GRAPHPATH
).
listFiles
((
dir
,
name
)
->
name
.
endsWith
(
".dgs"
));
if
(
dgsFiles
!=
null
)
{
getAllInfoGraph
(
dgsFiles
);
setUpComparatorOnlyMyAlgorithme
(
dgsFiles
);
setUpComparatorBetween
(
dgsFiles
);
}
}
/**
* Affiche les informations de base (nombre de nœuds et d'arêtes) pour chaque graphe.
* @param dgsFiles Tableau de fichiers .dgs représentant des graphes.
*/
public
static
void
getAllInfoGraph
(
File
[]
dgsFiles
)
{
for
(
File
file
:
dgsFiles
)
{
Graph
graphFile
=
getTheFileFromPath
(
file
.
getPath
());
System
.
out
.
println
(
file
.
getName
()
+
" : n Node("
+
graphFile
.
getNodeCount
()
+
") - n Edge("
+
graphFile
.
getEdgeCount
()+
")"
);
}
}
/**
* Effectue plusieurs tests du Bellman-Ford maison sur chaque graphe,
* avec des nœuds aléatoires, et sauvegarde les résultats dans des fichiers.
* @param dgsFiles Tableau de fichiers .dgs représentant des graphes.
*/
public
static
void
setUpComparatorOnlyMyAlgorithme
(
File
[]
dgsFiles
)
{
for
(
File
file
:
dgsFiles
)
{
System
.
out
.
println
(
file
.
getName
());
System
.
out
.
println
(
"=============================="
);
List
<
List
<
Double
>>
result
=
new
ArrayList
<>(
Collections
.
emptyList
());
Graph
graphFile
=
getTheFileFromPath
(
file
.
getPath
());
for
(
int
i
=
0
;
i
<
DEFAULT_TESTS_PER_GRAPH_MY_BELLMAN
;
i
++)
{
result
.
add
(
BellmanComparatorBetween
(
graphFile
,
getRandomNode
(
graphFile
),
getRandomNode
(
graphFile
),
false
));
}
String
filename
=
String
.
format
(
GNUPLOTPATH
+
"2_myBellman/"
+
(
file
.
getName
().
split
(
"\\."
)[
0
].
split
(
"_"
)[
1
])
+
".dat"
);
saveDat
(
filename
,
result
);
}
}
/**
* Effectue la comparaison entre l'algorithme maison et GraphStream pour chaque graphe.
* Les arêtes sont préalablement pondérées pour l'équité des tests.
* @param dgsFiles Tableau de fichiers .dgs représentant des graphes.
*/
public
static
void
setUpComparatorBetween
(
File
[]
dgsFiles
)
{
Arrays
.
sort
(
dgsFiles
,
(
a
,
b
)
->
a
.
getName
().
equals
(
"graphe_lehavre.dgs"
)
?
-
1
:
b
.
getName
().
equals
(
"graphe_lehavre.dgs"
)
?
1
:
a
.
getName
().
compareTo
(
b
.
getName
()));
// juste pour tester leHavre en premier car plus rapide
assert
dgsFiles
!=
null
;
for
(
File
file
:
dgsFiles
)
{
System
.
out
.
println
(
file
.
getName
());
System
.
out
.
println
(
"=============================="
);
List
<
List
<
Double
>>
result
=
new
ArrayList
<>(
Collections
.
emptyList
());
Graph
graphFile
=
getTheFileFromPath
(
file
.
getPath
());
// Ajout du poids avant de calculer le temps pour équiter entre algorithme
graphFile
.
edges
().
forEach
(
edge
->
{
double
cost
=
getCostOfTheArete
(
edge
);
edge
.
setAttribute
(
"weight"
,
cost
);
});
for
(
int
i
=
0
;
i
<
DEFAULT_TESTS_PER_GRAPHCOMPARATE
;
i
++)
{
result
.
add
(
BellmanComparatorBetween
(
graphFile
,
getRandomNode
(
graphFile
),
getRandomNode
(
graphFile
),
true
));
}
String
filename
=
String
.
format
(
GNUPLOTPATH
+
"3_compare/"
+
(
file
.
getName
().
split
(
"\\."
)[
0
].
split
(
"_"
)[
1
])
+
".dat"
);
saveDat
(
filename
,
result
);
}
}
/**
* Compare les temps d'exécution du Bellman-Ford maison et de GraphStream pour un même couple de nœuds.
* Les résultats sont renvoyés sous forme de liste de durées en millisecondes.
*
* @param graph Le graphe sur lequel effectuer les calculs.
* @param startNode Le nœud de départ.
* @param endNode Le nœud d'arrivée.
* @param withGraphStream Si vrai, exécute également l'algorithme GraphStream.
* @return Liste des durées (en ms) pour chaque algorithme exécuté.
*/
public
static
ArrayList
<
Double
>
BellmanComparatorBetween
(
Graph
graph
,
Node
startNode
,
Node
endNode
,
Boolean
withGraphStream
)
{
ArrayList
<
Double
>
results
=
new
ArrayList
<>();
System
.
out
.
println
(
"BellmanFord : Node("
+
startNode
.
getId
()
+
") -> Node("
+
endNode
.
getId
()
+
")"
);
if
(
graph
!=
null
)
{
ExecutorService
executor
=
Executors
.
newSingleThreadExecutor
();
// Pour le timeOut
try
{
if
(
withGraphStream
)
{
// Calcule le temps de BellmanFord de GRAPHSTREAM
Future
<
Double
>
futureGS
=
executor
.
submit
(()
->
{
long
startGS
=
System
.
nanoTime
();
BellmanFord
bf
=
new
BellmanFord
();
bf
.
init
(
graph
);
bf
.
setSource
(
startNode
.
getId
());
bf
.
compute
();
long
endGS
=
System
.
nanoTime
();
double
cost
=
bf
.
getShortestPathValue
(
endNode
);
System
.
out
.
println
(
"GraphStream Bellman-Ford = "
+
(
endGS
-
startGS
)
/
1_000_000.0
+
" ms"
);
results
.
add
((
endGS
-
startGS
)
/
1_000_000.0
);
return
cost
;
});
try
{
futureGS
.
get
(
DEFAULT_TIMEOUT_SECONDS
,
TimeUnit
.
SECONDS
);
}
catch
(
TimeoutException
e
)
{
System
.
out
.
println
(
"⚠ GraphStream Bellman-Ford > : timeout résultat fixé à "
+
DEFAULT_TIMEOUT_SECONDS
+
"s"
);
results
.
add
((
double
)
DEFAULT_TIMEOUT_SECONDS
*
1_000
);
}
}
// Calcule le temps de BellmanFord du Mien
Future
<
Float
>
futureMine
=
executor
.
submit
(()
->
{
long
startMine
=
System
.
nanoTime
();
BellmanFordResult
result
=
BellmanFord
(
graph
,
startNode
);
PathResult
pathResult
=
getPath
(
result
,
endNode
);
long
endMine
=
System
.
nanoTime
();
System
.
out
.
println
(
"Mon Bellman-Ford = "
+
(
endMine
-
startMine
)
/
1_000_000.0
+
" ms"
);
results
.
add
((
endMine
-
startMine
)
/
1_000_000.0
);
return
pathResult
.
cost
();
});
try
{
futureMine
.
get
(
DEFAULT_TIMEOUT_SECONDS
,
TimeUnit
.
SECONDS
);
}
catch
(
TimeoutException
e
)
{
System
.
out
.
println
(
"⚠ Mon Bellman-Ford > : timeout résultat fixé à "
+
DEFAULT_TIMEOUT_SECONDS
+
"s"
);
results
.
add
((
double
)
DEFAULT_TIMEOUT_SECONDS
*
1_000
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
return
results
;
}
}
src/test/java/org/example/Utils.java
0 → 100644
Voir le fichier @
27c612f7
package
org.example
;
import
org.graphstream.graph.Graph
;
import
org.graphstream.graph.Node
;
import
java.io.FileWriter
;
import
java.io.IOException
;
import
java.util.List
;
import
java.util.concurrent.ThreadLocalRandom
;
public
class
Utils
{
/**
* Sauvegarde un histogramme dans un fichier au format texte.
* Chaque ligne contient soit un seul élément, soit deux éléments séparés par un espace.
*
* @param filename Nom du fichier dans lequel écrire l'histogramme.
* @param histogram Liste de listes de durées (ou valeurs) à enregistrer.
*/
public
static
void
saveDat
(
String
filename
,
List
<
List
<
Double
>>
histogram
)
{
try
(
FileWriter
writer
=
new
FileWriter
(
filename
))
{
for
(
List
<
Double
>
durations
:
histogram
)
{
if
(
durations
.
size
()
==
1
)
{
writer
.
write
(
durations
.
get
(
0
)
+
"\n"
);
}
else
if
(
durations
.
size
()
>=
2
)
{
writer
.
write
(
durations
.
get
(
0
)
+
" "
+
durations
.
get
(
1
)
+
"\n"
);
}
}
System
.
out
.
println
(
"Histogramme enregistré dans "
+
filename
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
/**
* Sélectionne un nœud aléatoire dans un graphe donné.
*
* @param graph Le graphe dans lequel choisir un nœud.
* @return Un nœud choisi aléatoirement parmi tous les nœuds du graphe.
*/
public
static
Node
getRandomNode
(
Graph
graph
){
return
graph
.
getNode
(
ThreadLocalRandom
.
current
().
nextInt
(
0
,
graph
.
getNodeCount
()));
}
}
Précédent
1
2
Suivant