Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package fr.univ.dblp.analysis;
import fr.univ.dblp.export.DataExporter;
import fr.univ.dblp.export.ResultsPrinter;
import fr.univ.dblp.utils.RandomSampler;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import java.util.*;
/**
* Analyse les distances moyennes dans un réseau (Question 5).
*
* Cette classe calcule la distance moyenne entre les nœuds du réseau
* par échantillonnage et parcours en largeur (BFS). Elle permet également
* de vérifier l'hypothèse des "six degrés de séparation" et de comparer
* avec un réseau aléatoire théorique.
*
* @author Hamadou BA
* @see <a href="https://www-apps.univ-lehavre.fr/forge/bh243413/tp2-ri-mesures-de-reseaux-interaction.git">Dépôt Git</a>
*/
public class DistanceAnalyzer {
private static final int SAMPLE_SIZE = 1000;
/**
* Calcule la distance moyenne par échantillonnage et parcours BFS.
*
* Pour éviter de calculer toutes les paires de nœuds (O(N²)), cette méthode
* échantillonne aléatoirement SAMPLE_SIZE nœuds et effectue un BFS depuis
* chacun pour estimer la distance moyenne.
*
* @param g Le graphe à analyser
* @return La distance moyenne estimée
*/
public static double computeAverageDistance(Graph g) {
ResultsPrinter.printInfo(
String.format("Échantillonnage de %d nœuds pour estimer la distance moyenne...", SAMPLE_SIZE));
List<Node> sampledNodes = RandomSampler.sampleNodes(g, SAMPLE_SIZE);
// Utilisation de somme et compteur au lieu de stocker toutes les distances
double sum = 0.0;
long count = 0;
int progress = 0;
for (Node source : sampledNodes) {
Map<String, Integer> distancesFromSource = bfsDistances(g, source.getId());
for (int dist : distancesFromSource.values()) {
if (dist > 0) { // Exclure la distance à soi-même
sum += dist;
count++;
}
}
progress++;
if (progress % 100 == 0) {
ResultsPrinter.printInfo(
String.format(" Progression: %d/%d nœuds traités", progress, SAMPLE_SIZE));
}
}
if (count == 0) {
return 0.0;
}
return sum / count;
}
/**
* Calcule la distribution des distances à partir de nœuds échantillonnés.
*
* Cette méthode retourne une map associant chaque distance au nombre
* de paires de nœuds séparées par cette distance.
*
* @param g Le graphe à analyser
* @return Map distance -> fréquence
*/
public static Map<Integer, Integer> computeDistanceDistribution(Graph g) {
ResultsPrinter.printInfo("Calcul de la distribution des distances...");
List<Node> sampledNodes = RandomSampler.sampleNodes(g, SAMPLE_SIZE);
Map<Integer, Integer> distribution = new HashMap<>();
for (Node source : sampledNodes) {
Map<String, Integer> distancesFromSource = bfsDistances(g, source.getId());
for (int dist : distancesFromSource.values()) {
if (dist > 0) {
distribution.put(dist, distribution.getOrDefault(dist, 0) + 1);
}
}
}
return distribution;
}
/**
* Effectue un parcours en largeur (BFS) depuis un nœud source.
*
* Cette méthode privée calcule les distances depuis un nœud source
* vers tous les nœuds atteignables du graphe.
*
* @param g Le graphe
* @param sourceId Identifiant du nœud source
* @return Map associant chaque nœud atteignable à sa distance depuis la source
*/
private static Map<String, Integer> bfsDistances(Graph g, String sourceId) {
Map<String, Integer> distances = new HashMap<>();
Queue<String> queue = new LinkedList<>();
queue.add(sourceId);
distances.put(sourceId, 0);
while (!queue.isEmpty()) {
String currentId = queue.poll();
int currentDist = distances.get(currentId);
Node currentNode = g.getNode(currentId);
if (currentNode == null) continue;
for (Edge edge : currentNode.edges().toArray(Edge[]::new)) {
Node neighbor = edge.getOpposite(currentNode);
String neighborId = neighbor.getId();
if (!distances.containsKey(neighborId)) {
distances.put(neighborId, currentDist + 1);
queue.add(neighborId);
}
}
}
return distances;
}
/**
* Calcule la distance moyenne théorique pour un réseau aléatoire.
*
* Pour un réseau aléatoire, la distance moyenne théorique est:
* l ≈ ln(N) / ln(<k>)
*
* @param n Nombre de nœuds
* @param avgDegree Degré moyen
* @return Distance moyenne théorique
*/
public static double theoreticalRandomDistance(int n, double avgDegree) {
if (avgDegree <= 1) {
return Double.POSITIVE_INFINITY;
}
return Math.log(n) / Math.log(avgDegree);
}
/**
* Exporte la distribution des distances pour visualisation avec gnuplot.
*
* @param dist Distribution des distances (distance -> fréquence)
* @param filePath Chemin du fichier de sortie
*/
public static void exportDistanceDistribution(Map<Integer, Integer> dist, String filePath) {
DataExporter.exportDistanceDistribution(dist, filePath);
}
/**
* Effectue l'analyse complète de la Question 5.
*
* Cette méthode calcule la distance moyenne, la compare avec un réseau
* aléatoire théorique, vérifie l'hypothèse des "six degrés de séparation"
* et exporte la distribution des distances.
*
* @param g Le graphe à analyser
* @param networkName Nom du réseau (pour l'affichage)
* @param baseFilename Nom de base pour les fichiers de sortie
*/
public static void analyze(Graph g, String networkName, String baseFilename) {
long startTime = System.currentTimeMillis();
ResultsPrinter.printHeader("QUESTION 5: Distance moyenne - " + networkName);
// Calcul de la distance moyenne
double avgDistance = computeAverageDistance(g);
ResultsPrinter.printMetric("Distance moyenne (expérimentale)", avgDistance);
// Comparaison avec un réseau aléatoire théorique
int n = g.getNodeCount();
double avgDegree = BasicMetrics.getAverageDegree(g);
double theoreticalDist = theoreticalRandomDistance(n, avgDegree);
ResultsPrinter.printSeparator();
ResultsPrinter.printInfo("Comparaison avec réseau aléatoire théorique:");
ResultsPrinter.printMetric("Distance théorique (aléatoire)", theoreticalDist);
ResultsPrinter.printMetric("Différence", Math.abs(avgDistance - theoreticalDist));
// Vérification de l'hypothèse des "six degrés de séparation"
ResultsPrinter.printSeparator();
if (avgDistance <= 7.0) {
ResultsPrinter.printSuccess(
"Hypothèse des 'six degrés de séparation' CONFIRMÉE! (distance ≈ " +
String.format("%.1f", avgDistance) + ")");
} else {
ResultsPrinter.printInfo(
"Distance moyenne: " + String.format("%.1f", avgDistance) +
" (légèrement supérieure à 6)");
}
// Calcul et export de la distribution
ResultsPrinter.printSeparator();
Map<Integer, Integer> distribution = computeDistanceDistribution(g);
String distFile = "output/data/" + baseFilename + "_distances.dat";
exportDistanceDistribution(distribution, distFile);
// Recherche de la distance maximale
int maxDistance = distribution.keySet().stream().max(Integer::compareTo).orElse(0);
ResultsPrinter.printMetric("Distance maximale observée", maxDistance);
ResultsPrinter.printInfo("C'est un réseau 'petit monde' (small world)!");
ResultsPrinter.printElapsedTime(startTime);
}
}