# k p_k poisson_pk
0 0.0013598096 0.0013338133
1 0.0093386926 0.0088294616
2 0.0298558202 0.0292242521
3 0.0635910972 0.0644853901
4 0.1072249885 0.1067186988
5 0.1394604755 0.1412894381
6 0.1567580539 0.1558825956
7 0.1489791429 0.1474140132
8 0.1193432919 0.1219798131
9 0.0908072870 0.0897190435
10 0.0590517328 0.0593914346
11 0.0365548823 0.0357412973
12 0.0191373208 0.0197164284
13 0.0091987122 0.0100397772
14 0.0057191993 0.0047471748
15 0.0022596836 0.0020949957
16 0.0007998880 0.0008667669
17 0.0004799328 0.0003375146
18 0.0000399944 0.0001241250
19 0.0000199972 0.0000432459
20 0.0000199972 0.0000143138
# k p_k
0 0.0013598096
1 0.0093386926
2 0.0298558202
3 0.0635910972
4 0.1072249885
5 0.1394604755
6 0.1567580539
7 0.1489791429
8 0.1193432919
9 0.0908072870
10 0.0590517328
11 0.0365548823
12 0.0191373208
13 0.0091987122
14 0.0057191993
15 0.0022596836
16 0.0007998880
17 0.0004799328
18 0.0000399944
19 0.0000199972
20 0.0000199972
# distance frequency
1 6614
2 43997
3 290147
4 1866389
5 10226906
6 26207333
7 10700774
8 440999
9 5007
10 20
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fr.univ</groupId>
<artifactId>dblp-network-analysis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>DBLP Network Analysis</name>
<description>TP analyse réseau de collaboration DBLP avec GraphStream</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<graphstream.version>2.0</graphstream.version>
</properties>
<dependencies>
<!-- GraphStream Core -->
<dependency>
<groupId>org.graphstream</groupId>
<artifactId>gs-core</artifactId>
<version>${graphstream.version}</version>
</dependency>
<!-- GraphStream Algorithms -->
<dependency>
<groupId>org.graphstream</groupId>
<artifactId>gs-algo</artifactId>
<version>${graphstream.version}</version>
</dependency>
<!-- GraphStream UI Swing pour visualisation -->
<dependency>
<groupId>org.graphstream</groupId>
<artifactId>gs-ui-swing</artifactId>
<version>${graphstream.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<!-- Exec Plugin pour exécuter Main -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>fr.univ.dblp.Main</mainClass>
</configuration>
</plugin>
<!-- Assembly Plugin pour créer JAR exécutable -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<archive>
<manifest>
<mainClass>fr.univ.dblp.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package fr.univ.dblp;
import fr.univ.dblp.analysis.*;
import fr.univ.dblp.export.ResultsPrinter;
import fr.univ.dblp.generators.CopyGenerator;
import fr.univ.dblp.generators.NetworkGenerator;
import fr.univ.dblp.loader.GraphLoader;
import org.graphstream.graph.Graph;
import java.util.Scanner;
/**
* Point d'entrée principal pour l'analyse du réseau DBLP.
*
* Cette classe gère le menu interactif et orchestre l'exécution
* des différentes analyses du réseau de collaboration scientifique DBLP.
*
* @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 Main {
private static final String DBLP_FILE_PATH = "src/main/resources/snap/com-dblp.ungraph.txt";
private static Graph dblpGraph = null;
private static Graph baGraph = null;
public static void main(String[] args) {
ResultsPrinter.printHeader("TP: ANALYSE DU RÉSEAU DBLP");
ResultsPrinter.printInfo("Mesures de réseaux d'interaction - Collaboration scientifique");
ResultsPrinter.printInfo("Données: DBLP (Digital Bibliography & Library Project)");
System.out.println();
// Chargement initial du graphe DBLP
runQuestion1();
if (dblpGraph == null) {
ResultsPrinter.printError("Impossible de charger le graphe DBLP!");
ResultsPrinter.printError("Vérifiez que le fichier existe: " + DBLP_FILE_PATH);
return;
}
// Affichage du menu interactif
Scanner scanner = new Scanner(System.in);
boolean continueRunning = true;
while (continueRunning) {
displayMenu();
System.out.print("\nVotre choix: ");
String choice = scanner.nextLine().trim();
switch (choice) {
case "1":
runQuestion1();
break;
case "2":
runQuestion2();
break;
case "3":
runQuestion3();
break;
case "4":
runQuestion4();
break;
case "5":
runQuestion5();
break;
case "6":
runQuestion6();
break;
case "7":
runQuestion7();
break;
case "8":
runAllQuestions();
break;
case "0":
continueRunning = false;
ResultsPrinter.printInfo("Au revoir!");
break;
default:
ResultsPrinter.printWarning("Choix invalide! Essayez encore.");
}
if (continueRunning && !choice.equals("8")) {
System.out.println("\nAppuyez sur Entrée pour continuer...");
scanner.nextLine();
}
}
scanner.close();
}
/**
* Affiche le menu interactif principal.
*
* Ce menu permet à l'utilisateur de choisir quelle analyse exécuter
* parmi les 7 questions du TP ou d'exécuter toutes les analyses en séquence.
*/
private static void displayMenu() {
System.out.println("\n" + "=".repeat(80));
System.out.println("MENU PRINCIPAL");
System.out.println("=".repeat(80));
System.out.println(" 1. Question 1 - Chargement des données");
System.out.println(" 2. Question 2 - Mesures de base");
System.out.println(" 3. Question 3 - Analyse de connexité");
System.out.println(" 4. Question 4 - Distribution des degrés");
System.out.println(" 5. Question 5 - Distance moyenne");
System.out.println(" 6. Question 6 - Comparaison avec générateurs");
System.out.println(" 7. Question 7 - Générateur par copie (BONUS)");
System.out.println(" 8. TOUT EXÉCUTER (Questions 1-7)");
System.out.println(" 0. Quitter");
System.out.println("=".repeat(80));
}
/**
* Question 1: Charge les données du réseau DBLP.
*
* Cette méthode charge le graphe de collaboration DBLP depuis le fichier
* de données et affiche les informations de base sur le graphe chargé.
*/
private static void runQuestion1() {
ResultsPrinter.printHeader("QUESTION 1: Chargement des données");
dblpGraph = GraphLoader.loadDBLP(DBLP_FILE_PATH);
if (dblpGraph != null) {
ResultsPrinter.printSuccess("Graphe DBLP chargé avec succès!");
GraphLoader.displayGraphInfo(dblpGraph);
ResultsPrinter.printInfo("\nNote: La visualisation n'est pas recommandée pour ce graphe");
ResultsPrinter.printInfo(" (trop de nœuds: visualisation très lente et peu informative)");
}
}
/**
* Question 2: Calcule et affiche les mesures de base du réseau.
*
* Cette méthode calcule le nombre de nœuds, d'arêtes, le degré moyen
* et le coefficient de clustering du réseau DBLP.
*/
private static void runQuestion2() {
if (dblpGraph == null) {
ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
return;
}
BasicMetrics.analyze(dblpGraph, "DBLP");
}
/**
* Question 3: Analyse la connexité du réseau.
*
* Cette méthode vérifie si le réseau est connexe, compte le nombre de
* composantes connexes et détermine le degré critique pour la connexité.
*/
private static void runQuestion3() {
if (dblpGraph == null) {
ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
return;
}
ConnectivityAnalyzer.analyze(dblpGraph);
}
/**
* Question 4: Analyse la distribution des degrés du réseau.
*
* Cette méthode calcule la distribution des degrés, l'exporte pour
* visualisation avec gnuplot et vérifie si elle suit une loi de puissance.
*/
private static void runQuestion4() {
if (dblpGraph == null) {
ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
return;
}
DegreeAnalyzer.analyze(dblpGraph, "DBLP", "dblp");
ResultsPrinter.printInfo("\nPour générer les graphiques avec gnuplot:");
ResultsPrinter.printInfo(" cd gnuplot");
ResultsPrinter.printInfo(" gnuplot plot_dd_linear.gnu");
ResultsPrinter.printInfo(" gnuplot plot_dd_loglog.gnu");
ResultsPrinter.printInfo(" gnuplot plot_dd_powerlaw.gnu");
}
/**
* Question 5: Calcule la distance moyenne du réseau.
*
* Cette méthode effectue un échantillonnage de 1000 nœuds et calcule
* la distance moyenne par parcours BFS. Attention: opération longue (15-25 min).
*/
private static void runQuestion5() {
if (dblpGraph == null) {
ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
return;
}
ResultsPrinter.printWarning("ATTENTION: Cette analyse peut prendre 15-25 minutes!");
ResultsPrinter.printInfo("(1000 parcours BFS sur un graphe de 317k nœuds)");
System.out.print("\nContinuer? (o/n): ");
Scanner scanner = new Scanner(System.in);
String response = scanner.nextLine().trim().toLowerCase();
if (response.equals("o") || response.equals("oui") || response.equals("y") || response.equals("yes")) {
DistanceAnalyzer.analyze(dblpGraph, "DBLP", "dblp");
ResultsPrinter.printInfo("\nPour générer le graphique avec gnuplot:");
ResultsPrinter.printInfo(" cd gnuplot && gnuplot plot_distances.gnu");
} else {
ResultsPrinter.printInfo("Analyse annulée.");
}
}
/**
* Question 6: Compare le réseau DBLP avec des générateurs de réseaux.
*
* Cette méthode génère un réseau aléatoire (Erdős-Rényi) et un réseau
* Barabási-Albert, puis compare leurs propriétés avec DBLP. Durée: 30-40 min.
*/
private static void runQuestion6() {
if (dblpGraph == null) {
ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
return;
}
ResultsPrinter.printWarning("ATTENTION: Cette analyse peut prendre 30-40 minutes!");
ResultsPrinter.printInfo("(Génération et analyse de 2 grands réseaux)");
System.out.print("\nContinuer? (o/n): ");
Scanner scanner = new Scanner(System.in);
String response = scanner.nextLine().trim().toLowerCase();
if (response.equals("o") || response.equals("oui") || response.equals("y") || response.equals("yes")) {
NetworkGenerator.analyze(dblpGraph);
ResultsPrinter.printInfo("\nPour générer le graphique comparatif:");
ResultsPrinter.printInfo(" cd gnuplot && gnuplot plot_comparison_networks.gnu");
} else {
ResultsPrinter.printInfo("Analyse annulée.");
}
}
/**
* Question 7 (BONUS): Teste le générateur par copie.
*
* Cette méthode génère un réseau avec le mécanisme de copie et compare
* son clustering avec DBLP et Barabási-Albert. Durée: 15-20 min.
*/
private static void runQuestion7() {
if (dblpGraph == null) {
ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
return;
}
// Génération du graphe BA si nécessaire pour la comparaison
if (baGraph == null) {
ResultsPrinter.printInfo("Génération du graphe Barabási-Albert pour comparaison...");
int n = Math.min(dblpGraph.getNodeCount(), 50000);
double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);
int edgesPerNode = (int) Math.round(avgDegree / 2.0);
baGraph = NetworkGenerator.generateBarabasiAlbert(n, Math.max(1, edgesPerNode));
}
ResultsPrinter.printWarning("ATTENTION: Cette analyse peut prendre 15-20 minutes!");
System.out.print("\nContinuer? (o/n): ");
Scanner scanner = new Scanner(System.in);
String response = scanner.nextLine().trim().toLowerCase();
if (response.equals("o") || response.equals("oui") || response.equals("y") || response.equals("yes")) {
CopyGenerator.analyze(dblpGraph, baGraph);
} else {
ResultsPrinter.printInfo("Analyse annulée.");
}
}
/**
* Exécute toutes les questions en séquence.
*
* Cette méthode lance l'exécution automatique de toutes les analyses
* (Questions 1 à 7) sans interruption. Durée totale: environ 1h30.
*/
private static void runAllQuestions() {
ResultsPrinter.printHeader("EXÉCUTION COMPLÈTE - TOUTES LES QUESTIONS");
ResultsPrinter.printWarning("ATTENTION: L'exécution complète peut prendre 1h30 à 2h!");
ResultsPrinter.printInfo("Les questions longues (Q5, Q6, Q7) seront exécutées automatiquement.");
System.out.print("\nVoulez-vous vraiment tout exécuter? (o/n): ");
Scanner scanner = new Scanner(System.in);
String response = scanner.nextLine().trim().toLowerCase();
if (!response.equals("o") && !response.equals("oui") && !response.equals("y") && !response.equals("yes")) {
ResultsPrinter.printInfo("Exécution annulée.");
return;
}
long globalStart = System.currentTimeMillis();
// Question 1: Chargement
if (dblpGraph == null) {
runQuestion1();
}
// Question 2: Mesures de base
if (dblpGraph != null) {
runQuestion2();
}
// Question 3: Connexité
if (dblpGraph != null) {
runQuestion3();
}
// Question 4: Distribution des degrés
if (dblpGraph != null) {
runQuestion4();
}
// Question 5: Distance moyenne
if (dblpGraph != null) {
ResultsPrinter.printInfo("\n>>> Démarrage Question 5 (Distance moyenne)...");
DistanceAnalyzer.analyze(dblpGraph, "DBLP", "dblp");
}
// Question 6: Comparaison avec générateurs
if (dblpGraph != null) {
ResultsPrinter.printInfo("\n>>> Démarrage Question 6 (Générateurs)...");
NetworkGenerator.analyze(dblpGraph);
}
// Question 7: Générateur par copie (BONUS)
if (dblpGraph != null) {
ResultsPrinter.printInfo("\n>>> Démarrage Question 7 (Générateur copie - BONUS)...");
// Génération du graphe BA si nécessaire
if (baGraph == null) {
int n = Math.min(dblpGraph.getNodeCount(), 50000);
double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);
int edgesPerNode = (int) Math.round(avgDegree / 2.0);
baGraph = NetworkGenerator.generateBarabasiAlbert(n, Math.max(1, edgesPerNode));
}
CopyGenerator.analyze(dblpGraph, baGraph);
}
ResultsPrinter.printHeader("TOUTES LES ANALYSES TERMINÉES!");
ResultsPrinter.printElapsedTime(globalStart);
ResultsPrinter.printInfo("\nPROCHAINES ÉTAPES:");
ResultsPrinter.printInfo(" 1. Générez les graphiques avec gnuplot (voir gnuplot/*.gnu)");
ResultsPrinter.printInfo(" 2. Consultez les fichiers de données dans output/data/");
ResultsPrinter.printInfo(" 3. Rédigez le rapport README.md avec les résultats et graphiques");
}
}
package fr.univ.dblp.analysis;
import fr.univ.dblp.export.ResultsPrinter;
import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Graph;
/**
* Calcule les métriques de base d'un réseau (Question 2).
*
* Cette classe permet de calculer et d'afficher les mesures fondamentales
* d'un réseau: nombre de nœuds, nombre d'arêtes, degré moyen et coefficient
* de clustering. Elle compare également ces métriques avec un réseau aléatoire
* théorique de même taille.
*
* @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 BasicMetrics {
/**
* Retourne le nombre de nœuds du graphe.
*
* @param g Le graphe à analyser
* @return Le nombre de nœuds
*/
public static int getNodeCount(Graph g) {
return g.getNodeCount();
}
/**
* Retourne le nombre d'arêtes du graphe.
*
* @param g Le graphe à analyser
* @return Le nombre d'arêtes
*/
public static int getEdgeCount(Graph g) {
return g.getEdgeCount();
}
/**
* Calcule le degré moyen du graphe.
*
* @param g Le graphe à analyser
* @return Le degré moyen
*/
public static double getAverageDegree(Graph g) {
return Toolkit.averageDegree(g);
}
/**
* Calcule le coefficient de clustering du graphe.
*
* Le coefficient de clustering mesure la probabilité que deux voisins
* d'un nœud soient également voisins entre eux (formation de triangles).
*
* @param g Le graphe à analyser
* @return Le coefficient de clustering moyen
*/
public static double getClusteringCoefficient(Graph g) {
return Toolkit.averageClusteringCoefficient(g);
}
/**
* Calcule le coefficient de clustering théorique d'un réseau aléatoire.
*
* Pour un réseau aléatoire de même taille et même degré moyen,
* le coefficient de clustering théorique est: C = <k> / N
*
* @param n Nombre de nœuds du réseau
* @param avgDegree Degré moyen du réseau
* @return Coefficient de clustering théorique
*/
public static double theoreticalRandomClusteringCoeff(int n, double avgDegree) {
return avgDegree / n;
}
/**
* Affiche toutes les métriques de base d'un réseau.
*
* Cette méthode calcule et affiche le nombre de nœuds, d'arêtes,
* le degré moyen et le coefficient de clustering. Elle compare
* également le clustering avec un réseau aléatoire théorique.
*
* @param g Le graphe à analyser
* @param networkName Le nom du réseau (pour l'affichage)
*/
public static void printMetrics(Graph g, String networkName) {
ResultsPrinter.printHeader("QUESTION 2: Mesures de base - " + networkName);
int nodes = getNodeCount(g);
int edges = getEdgeCount(g);
double avgDegree = getAverageDegree(g);
double clustering = getClusteringCoefficient(g);
ResultsPrinter.printMetric("Nombre de nœuds", nodes);
ResultsPrinter.printMetric("Nombre d'arêtes", edges);
ResultsPrinter.printMetric("Degré moyen", avgDegree);
ResultsPrinter.printMetric("Coefficient de clustering", clustering);
// Comparaison avec un réseau aléatoire théorique
double theoreticalClustering = theoreticalRandomClusteringCoeff(nodes, avgDegree);
ResultsPrinter.printSeparator();
ResultsPrinter.printInfo("Comparaison avec un réseau aléatoire de même taille:");
ResultsPrinter.printMetric("Clustering théorique (aléatoire)", theoreticalClustering);
double ratio = clustering / theoreticalClustering;
ResultsPrinter.printMetric("Ratio (DBLP / aléatoire)", ratio);
if (ratio > 1000) {
ResultsPrinter.printInfo(
String.format("Le clustering du réseau DBLP est %.0fx plus élevé qu'un réseau aléatoire!",
ratio));
}
}
/**
* Analyse et affiche les métriques de base d'un graphe.
*
* Cette méthode orchestre l'analyse complète en appelant printMetrics()
* et en affichant le temps d'exécution total.
*
* @param g Le graphe à analyser
* @param networkName Le nom du réseau (pour l'affichage)
*/
public static void analyze(Graph g, String networkName) {
long startTime = System.currentTimeMillis();
printMetrics(g, networkName);
ResultsPrinter.printElapsedTime(startTime);
}
}
package fr.univ.dblp.analysis;
import fr.univ.dblp.export.ResultsPrinter;
import org.graphstream.algorithm.ConnectedComponents;
import org.graphstream.graph.Graph;
import org.graphstream.graph.implementations.SingleGraph;
import org.graphstream.algorithm.generator.RandomGenerator;
/**
* Analyse la connexité d'un réseau (Question 3).
*
* Cette classe permet de vérifier si un réseau est connexe, de compter
* le nombre de composantes connexes et de déterminer le degré critique
* pour la connexité d'un réseau aléatoire.
*
* @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 ConnectivityAnalyzer {
/**
* Vérifie si le graphe est connexe.
*
* Un graphe est connexe s'il existe un chemin entre toute paire de nœuds,
* c'est-à-dire s'il ne contient qu'une seule composante connexe.
*
* @param g Le graphe à analyser
* @return true si le graphe est connexe, false sinon
*/
public static boolean isConnected(Graph g) {
ConnectedComponents cc = new ConnectedComponents();
cc.init(g);
cc.compute();
return cc.getConnectedComponentsCount() == 1;
}
/**
* Compte le nombre de composantes connexes du graphe.
*
* Une composante connexe est un sous-ensemble maximal de nœuds
* tel qu'il existe un chemin entre toute paire de nœuds de ce sous-ensemble.
*
* @param g Le graphe à analyser
* @return Le nombre de composantes connexes
*/
public static int countConnectedComponents(Graph g) {
ConnectedComponents cc = new ConnectedComponents();
cc.init(g);
cc.compute();
return cc.getConnectedComponentsCount();
}
/**
* Génère un réseau aléatoire avec une taille et un degré moyen donnés.
*
* Utilise le modèle d'Erdős-Rényi pour créer un graphe aléatoire
* où les arêtes sont créées aléatoirement.
*
* @param n Nombre de nœuds du réseau
* @param avgDegree Degré moyen souhaité
* @return Le graphe aléatoire généré
*/
public static Graph generateRandomGraph(int n, double avgDegree) {
Graph g = new SingleGraph("Random");
g.setStrict(false);
g.setAutoCreate(true);
// Nombre d'arêtes nécessaires: E = (N × degré_moyen) / 2
int targetEdges = (int) Math.round((n * avgDegree) / 2.0);
// Utilisation du générateur aléatoire de GraphStream
RandomGenerator gen = new RandomGenerator(avgDegree);
gen.addSink(g);
gen.begin();
// Génération des nœuds et arêtes
for (int i = 0; i < n && g.getEdgeCount() < targetEdges; i++) {
gen.nextEvents();
}
gen.end();
return g;
}
/**
* Trouve le degré critique pour la connexité d'un réseau aléatoire.
*
* Utilise une recherche binaire pour déterminer le degré moyen minimum
* au-dessus duquel un réseau aléatoire devient connexe. La théorie prédit
* que ce degré critique est approximativement ln(N).
*
* @param nodeCount Nombre de nœuds du réseau
* @return Le degré critique expérimental
*/
public static double findCriticalAverageDegree(int nodeCount) {
ResultsPrinter.printInfo("Recherche du degré critique pour la connexité...");
ResultsPrinter.printInfo("(Cela peut prendre quelques minutes)");
double theoreticalCritical = Math.log(nodeCount);
ResultsPrinter.printInfo(
String.format("Degré critique théorique: %.2f (ln(%,d))", theoreticalCritical, nodeCount));
// Utilisation d'une taille réduite pour accélérer le calcul
int sampleSize = Math.min(nodeCount, 10000);
double low = 1.0;
double high = theoreticalCritical * 2;
double epsilon = 0.1;
while (high - low > epsilon) {
double mid = (low + high) / 2.0;
ResultsPrinter.printInfo(String.format(" Test degré moyen = %.2f", mid));
// Tests multiples pour plus de fiabilité statistique
int connectedCount = 0;
int trials = 5;
for (int trial = 0; trial < trials; trial++) {
Graph testGraph = generateRandomGraph(sampleSize, mid);
if (isConnected(testGraph)) {
connectedCount++;
}
}
// Si la majorité sont connexes, on teste avec un degré plus faible
if (connectedCount >= trials / 2) {
high = mid;
} else {
low = mid;
}
}
double critical = (low + high) / 2.0;
ResultsPrinter.printSuccess(String.format("Degré critique trouvé: %.2f", critical));
return critical;
}
/**
* Analyse la connexité d'un graphe et affiche les résultats.
*
* @param g Le graphe à analyser
* @param networkName Le nom du réseau (pour l'affichage)
*/
public static void analyzeConnectivity(Graph g, String networkName) {
ResultsPrinter.printSubHeader("Analyse de connexité - " + networkName);
boolean connected = isConnected(g);
int componentCount = countConnectedComponents(g);
ResultsPrinter.printMetric("Graphe connexe", connected ? "Oui" : "Non");
ResultsPrinter.printMetric("Nombre de composantes connexes", componentCount);
if (!connected) {
ResultsPrinter.printWarning("Le graphe n'est pas connexe!");
}
}
/**
* Effectue l'analyse complète de la Question 3.
*
* Cette méthode analyse la connexité du réseau DBLP, génère et teste
* un réseau aléatoire équivalent, et détermine le degré critique
* pour la connexité.
*
* @param dblpGraph Le graphe DBLP à analyser
*/
public static void analyze(Graph dblpGraph) {
long startTime = System.currentTimeMillis();
ResultsPrinter.printHeader("QUESTION 3: Analyse de connexité");
// 1. Vérification de la connexité de DBLP
analyzeConnectivity(dblpGraph, "DBLP");
// 2. Génération et test d'un réseau aléatoire avec les mêmes paramètres
ResultsPrinter.printSubHeader("Réseau aléatoire (même taille et degré moyen)");
int n = dblpGraph.getNodeCount();
double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);
ResultsPrinter.printInfo(
String.format("Génération d'un réseau aléatoire: N=%,d, <k>=%.2f", n, avgDegree));
// Utilisation d'une taille réduite pour accélérer le calcul
int randomSize = Math.min(n, 50000);
ResultsPrinter.printInfo(
String.format("(Utilisation d'une taille réduite pour le calcul: N=%,d)", randomSize));
Graph randomGraph = generateRandomGraph(randomSize, avgDegree);
analyzeConnectivity(randomGraph, "Aléatoire");
// 3. Recherche du degré critique
ResultsPrinter.printSubHeader("Degré critique pour connexité");
double critical = findCriticalAverageDegree(randomSize);
double theoreticalCritical = Math.log(randomSize);
ResultsPrinter.printSeparator();
ResultsPrinter.printMetric("Degré critique (expérimental)", critical);
ResultsPrinter.printMetric("Degré critique (théorique ln(N))", theoreticalCritical);
ResultsPrinter.printMetric("Différence", Math.abs(critical - theoreticalCritical));
ResultsPrinter.printElapsedTime(startTime);
}
}
package fr.univ.dblp.analysis;
import fr.univ.dblp.export.DataExporter;
import fr.univ.dblp.export.ResultsPrinter;
import org.graphstream.algorithm.Toolkit;
import org.graphstream.graph.Graph;
/**
* Analyse la distribution des degrés d'un réseau (Question 4).
*
* Cette classe calcule la distribution des degrés, l'exporte pour
* visualisation avec gnuplot et la compare avec une distribution de Poisson
* théorique pour identifier si le réseau suit une loi de puissance.
*
* @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 DegreeAnalyzer {
/**
* Calcule la distribution des degrés du graphe.
*
* Retourne un tableau où l'indice représente le degré et la valeur
* représente le nombre de nœuds ayant ce degré.
*
* @param g Le graphe à analyser
* @return Tableau de la distribution des degrés
*/
public static int[] getDegreeDistribution(Graph g) {
return Toolkit.degreeDistribution(g);
}
/**
* Normalise la distribution pour obtenir des probabilités.
*
* Chaque valeur est divisée par le nombre total de nœuds pour obtenir
* la probabilité qu'un nœud ait un degré donné.
*
* @param dd Distribution des degrés (nombre de nœuds par degré)
* @param nodeCount Nombre total de nœuds
* @return Distribution normalisée (probabilités)
*/
public static double[] normalizeDistribution(int[] dd, int nodeCount) {
double[] normalized = new double[dd.length];
for (int i = 0; i < dd.length; i++) {
normalized[i] = (double) dd[i] / nodeCount;
}
return normalized;
}
/**
* Exporte la distribution des degrés pour visualisation avec gnuplot.
*
* @param g Le graphe à analyser
* @param outputFile Chemin du fichier de sortie
*/
public static void exportForGnuplot(Graph g, String outputFile) {
int[] dd = getDegreeDistribution(g);
int nodeCount = g.getNodeCount();
DataExporter.exportDegreeDistribution(dd, nodeCount, outputFile);
}
/**
* Exporte la distribution avec comparaison à une distribution de Poisson.
*
* Permet de comparer la distribution réelle avec celle d'un réseau
* aléatoire théorique (distribution de Poisson).
*
* @param g Le graphe à analyser
* @param outputFile Chemin du fichier de sortie
*/
public static void exportWithPoisson(Graph g, String outputFile) {
int[] dd = getDegreeDistribution(g);
int nodeCount = g.getNodeCount();
double avgDegree = BasicMetrics.getAverageDegree(g);
DataExporter.exportDegreeDistributionWithPoisson(dd, nodeCount, avgDegree, outputFile);
}
/**
* Analyse la distribution des degrés et affiche les statistiques.
*
* @param g Le graphe à analyser
* @param networkName Nom du réseau (pour l'affichage)
*/
public static void analyzeDistribution(Graph g, String networkName) {
ResultsPrinter.printSubHeader("Distribution des degrés - " + networkName);
int[] dd = getDegreeDistribution(g);
int nodeCount = g.getNodeCount();
// Recherche du degré maximum
int maxDegree = dd.length - 1;
ResultsPrinter.printMetric("Degré maximum", maxDegree);
// Comptage des nœuds avec degrés spécifiques
int degreeZero = dd.length > 0 ? dd[0] : 0;
ResultsPrinter.printMetric("Nœuds de degré 0", degreeZero);
if (degreeZero > 0) {
ResultsPrinter.printWarning(
String.format("%d nœuds isolés détectés!", degreeZero));
}
}
/**
* Effectue l'analyse complète de la Question 4.
*
* Cette méthode analyse la distribution des degrés, exporte les données
* pour gnuplot et fournit des indications pour l'interprétation.
*
* @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 4: Distribution des degrés - " + networkName);
analyzeDistribution(g, networkName);
// Export pour gnuplot
ResultsPrinter.printInfo("Export des données pour gnuplot...");
String degreeFile = "output/data/" + baseFilename + "_degree_distribution.dat";
String comparisonFile = "output/data/" + baseFilename + "_degree_comparison.dat";
exportForGnuplot(g, degreeFile);
exportWithPoisson(g, comparisonFile);
ResultsPrinter.printInfo("Observations attendues en échelle log-log:");
ResultsPrinter.printInfo(" - Ligne droite = loi de puissance p_k = C * k^(-γ)");
ResultsPrinter.printInfo(" - Utilisez gnuplot pour tracer et ajuster la loi");
ResultsPrinter.printElapsedTime(startTime);
}
}
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);
}
}
package fr.univ.dblp.export;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.Map;
/**
* Classe utilitaire pour l'export de données vers des fichiers (pour gnuplot).
*
* Cette classe fournit des méthodes pour exporter différents types de données
* (distributions de degrés, distributions de distances, etc.) dans des formats
* compatibles avec gnuplot pour la visualisation.
*
* @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 DataExporter {
/**
* Exporte des données à deux colonnes vers un fichier.
*
* Cette méthode générique permet d'exporter n'importe quelle paire de données
* (x, y) dans un format lisible par gnuplot.
*
* @param x Valeurs de la première colonne
* @param y Valeurs de la deuxième colonne
* @param filePath Chemin du fichier de sortie
* @param header En-tête optionnel (commentaire)
*/
public static void exportTwoColumns(double[] x, double[] y, String filePath, String header) {
try {
createDirectoryIfNeeded(filePath);
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
if (header != null && !header.isEmpty()) {
writer.println("# " + header);
}
for (int i = 0; i < Math.min(x.length, y.length); i++) {
writer.printf(Locale.US, "%.10f %.10f%n", x[i], y[i]);
}
}
ResultsPrinter.printSuccess("Données exportées vers: " + filePath);
} catch (IOException e) {
ResultsPrinter.printError("Erreur lors de l'export vers " + filePath + ": " + e.getMessage());
}
}
/**
* Exporte la distribution des degrés vers un fichier pour gnuplot.
*
* Le fichier contient deux colonnes: le degré k et la probabilité p_k
* qu'un nœud ait ce degré.
*
* @param degreeDistribution Tableau où l'indice = degré, valeur = nombre de nœuds
* @param nodeCount Nombre total de nœuds
* @param filePath Chemin du fichier de sortie
*/
public static void exportDegreeDistribution(int[] degreeDistribution, int nodeCount, String filePath) {
try {
createDirectoryIfNeeded(filePath);
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
writer.println("# k p_k");
for (int k = 0; k < degreeDistribution.length; k++) {
if (degreeDistribution[k] > 0) {
double pk = (double) degreeDistribution[k] / nodeCount;
writer.printf(Locale.US, "%d %.10f%n", k, pk);
}
}
}
ResultsPrinter.printSuccess("Distribution des degrés exportée vers: " + filePath);
} catch (IOException e) {
ResultsPrinter.printError("Erreur lors de l'export: " + e.getMessage());
}
}
/**
* Exporte la distribution des degrés avec comparaison à une distribution de Poisson.
*
* Le fichier contient trois colonnes: le degré k, la probabilité observée p_k
* et la probabilité théorique selon une distribution de Poisson.
*
* @param degreeDistribution Tableau où l'indice = degré, valeur = nombre de nœuds
* @param nodeCount Nombre total de nœuds
* @param avgDegree Degré moyen (paramètre lambda pour Poisson)
* @param filePath Chemin du fichier de sortie
*/
public static void exportDegreeDistributionWithPoisson(
int[] degreeDistribution, int nodeCount, double avgDegree, String filePath) {
try {
createDirectoryIfNeeded(filePath);
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
writer.println("# k p_k poisson_pk");
for (int k = 0; k < degreeDistribution.length; k++) {
if (degreeDistribution[k] > 0) {
double pk = (double) degreeDistribution[k] / nodeCount;
double poissonPk = poissonProbability(k, avgDegree);
writer.printf(Locale.US, "%d %.10f %.10f%n", k, pk, poissonPk);
}
}
}
ResultsPrinter.printSuccess("Distribution avec Poisson exportée vers: " + filePath);
} catch (IOException e) {
ResultsPrinter.printError("Erreur lors de l'export: " + e.getMessage());
}
}
/**
* Exporte la distribution des distances vers un fichier.
*
* Le fichier contient deux colonnes: la distance et la fréquence
* (nombre de paires de nœuds à cette distance).
*
* @param distanceDistribution Map associant distance -> fréquence
* @param filePath Chemin du fichier de sortie
*/
public static void exportDistanceDistribution(Map<Integer, Integer> distanceDistribution, String filePath) {
try {
createDirectoryIfNeeded(filePath);
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
writer.println("# distance frequency");
distanceDistribution.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> {
writer.printf(Locale.US, "%d %d%n", entry.getKey(), entry.getValue());
});
}
ResultsPrinter.printSuccess("Distribution des distances exportée vers: " + filePath);
} catch (IOException e) {
ResultsPrinter.printError("Erreur lors de l'export: " + e.getMessage());
}
}
/**
* Calcule la probabilité selon une distribution de Poisson.
*
* Formule: P(k) = (lambda^k * e^(-lambda)) / k!
* Utilise les logarithmes pour éviter les débordements numériques.
*
* @param k Valeur pour laquelle calculer la probabilité
* @param lambda Paramètre de la distribution (moyenne)
* @return Probabilité P(k)
*/
private static double poissonProbability(int k, double lambda) {
// Utilisation des logarithmes pour éviter les débordements
double logProb = k * Math.log(lambda) - lambda - logFactorial(k);
return Math.exp(logProb);
}
/**
* Calcule le logarithme de la factorielle.
*
* Méthode utilisée pour éviter les débordements lors du calcul
* de la probabilité de Poisson pour de grandes valeurs de k.
*
* @param n Nombre dont on calcule log(n!)
* @return log(n!)
*/
private static double logFactorial(int n) {
if (n <= 1) return 0.0;
double result = 0.0;
for (int i = 2; i <= n; i++) {
result += Math.log(i);
}
return result;
}
/**
* Crée le répertoire parent si nécessaire.
*
* Cette méthode garantit que le répertoire de destination existe
* avant d'écrire un fichier.
*
* @param filePath Chemin du fichier (le répertoire parent sera créé)
*/
private static void createDirectoryIfNeeded(String filePath) {
File file = new File(filePath);
File parentDir = file.getParentFile();
if (parentDir != null && !parentDir.exists()) {
parentDir.mkdirs();
}
}
}
package fr.univ.dblp.export;
import java.util.Locale;
/**
* Classe utilitaire pour l'affichage formaté des résultats dans la console.
*
* Cette classe fournit des méthodes pour afficher de manière cohérente
* les en-têtes, métriques, messages et tableaux comparatifs dans la console.
*
* @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 ResultsPrinter {
private static final String SEPARATOR = "=".repeat(80);
private static final String LINE = "-".repeat(80);
/**
* Affiche un en-tête de section principale.
*
* @param title Titre de la section
*/
public static void printHeader(String title) {
System.out.println("\n" + SEPARATOR);
System.out.println(" " + title);
System.out.println(SEPARATOR);
}
/**
* Affiche un en-tête de sous-section.
*
* @param subtitle Titre de la sous-section
*/
public static void printSubHeader(String subtitle) {
System.out.println("\n" + LINE);
System.out.println(" " + subtitle);
System.out.println(LINE);
}
/**
* Affiche une métrique avec son nom et sa valeur.
*
* La méthode formate automatiquement la valeur selon son type
* (Double, Integer, Long ou autre).
*
* @param name Nom de la métrique
* @param value Valeur de la métrique
*/
public static void printMetric(String name, Object value) {
if (value instanceof Double) {
System.out.printf(Locale.US, " %-40s: %.6f%n", name, (Double) value);
} else if (value instanceof Integer) {
System.out.printf(Locale.US, " %-40s: %,d%n", name, (Integer) value);
} else if (value instanceof Long) {
System.out.printf(Locale.US, " %-40s: %,d%n", name, (Long) value);
} else {
System.out.printf(Locale.US, " %-40s: %s%n", name, value);
}
}
/**
* Affiche une ligne de séparation.
*/
public static void printSeparator() {
System.out.println(LINE);
}
/**
* Affiche l'en-tête d'un tableau comparatif.
*
* @param networkNames Noms des réseaux à comparer (colonnes du tableau)
*/
public static void printComparisonHeader(String... networkNames) {
System.out.printf(Locale.US, "\n%-30s", "Metric");
for (String name : networkNames) {
System.out.printf(Locale.US, " | %-20s", name);
}
System.out.println();
System.out.println(SEPARATOR);
}
/**
* Affiche une ligne de données dans un tableau comparatif.
*
* @param metricName Nom de la métrique (première colonne)
* @param values Valeurs pour chaque réseau
*/
public static void printComparisonRow(String metricName, Object... values) {
System.out.printf(Locale.US, "%-30s", metricName);
for (Object value : values) {
if (value instanceof Double) {
System.out.printf(Locale.US, " | %-20.6f", (Double) value);
} else if (value instanceof Integer) {
String formatted = String.format(Locale.US, "%,d", (Integer) value);
System.out.printf(Locale.US, " | %-20s", formatted);
} else {
System.out.printf(Locale.US, " | %-20s", value);
}
}
System.out.println();
}
/**
* Affiche un message d'information.
*
* @param message Le message à afficher
*/
public static void printInfo(String message) {
System.out.println(" [INFO] " + message);
}
/**
* Affiche un message de succès.
*
* @param message Le message à afficher
*/
public static void printSuccess(String message) {
System.out.println(" [SUCCESS] " + message);
}
/**
* Affiche un message d'avertissement.
*
* @param message Le message à afficher
*/
public static void printWarning(String message) {
System.out.println(" [WARNING] " + message);
}
/**
* Affiche un message d'erreur.
*
* @param message Le message à afficher
*/
public static void printError(String message) {
System.err.println(" [ERROR] " + message);
}
/**
* Affiche le temps écoulé depuis un instant de départ.
*
* Le temps est affiché en secondes (si < 60s) ou en minutes et secondes.
*
* @param startTime Instant de départ (en millisecondes, obtenu via System.currentTimeMillis())
*/
public static void printElapsedTime(long startTime) {
long elapsed = System.currentTimeMillis() - startTime;
double seconds = elapsed / 1000.0;
if (seconds < 60) {
System.out.printf(Locale.US, " Temps écoulé: %.2f secondes%n", seconds);
} else {
int minutes = (int) (seconds / 60);
double remainingSeconds = seconds - (minutes * 60);
System.out.printf(Locale.US, " Temps écoulé: %d minutes %.2f secondes%n",
minutes, remainingSeconds);
}
}
}
package fr.univ.dblp.generators;
import fr.univ.dblp.analysis.BasicMetrics;
import fr.univ.dblp.analysis.DegreeAnalyzer;
import fr.univ.dblp.export.ResultsPrinter;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.SingleGraph;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Générateur de réseau par mécanisme de copie (Question 7 - Bonus).
*
* Algorithme:
* 1. Un nouveau nœud choisit un nœud aléatoire v
* 2. Il se connecte à chaque voisin de v avec une probabilité p
* 3. Il se connecte toujours à v lui-même
*
* Ce mécanisme crée naturellement des triangles (clustering élevé) car
* si le nouveau nœud se connecte à deux voisins de v qui sont eux-mêmes
* connectés, un triangle est formé.
*
* @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 CopyGenerator {
private double connectionProbability;
private Random random;
public CopyGenerator(double p) {
this.connectionProbability = p;
this.random = new Random();
}
/**
* Génère un graphe en utilisant le mécanisme de copie.
*
* Cette méthode implémente l'algorithme de copie qui favorise la formation
* de triangles et donc un coefficient de clustering élevé.
*
* @param targetNodes Nombre de nœuds cible
* @param p Probabilité de connexion aux voisins
* @return Le graphe généré
*/
public static Graph generateGraph(int targetNodes, double p) {
ResultsPrinter.printInfo(
String.format("Génération réseau par copie: N=%,d, p=%.2f", targetNodes, p));
Graph g = new SingleGraph("Copy-Model");
g.setStrict(false);
g.setAutoCreate(true);
// Initialisation avec quelques nœuds connectés (réseau de départ)
int seedSize = Math.min(10, targetNodes);
for (int i = 0; i < seedSize; i++) {
g.addNode("n" + i);
}
// Création des connexions initiales (petite clique)
for (int i = 0; i < seedSize; i++) {
for (int j = i + 1; j < Math.min(i + 3, seedSize); j++) {
g.addEdge("e_init_" + i + "_" + j, "n" + i, "n" + j);
}
}
Random rand = new Random();
int currentNodeCount = seedSize;
// Génération des nœuds restants avec le mécanisme de copie
while (currentNodeCount < targetNodes) {
// Choix d'un nœud aléatoire v PARMI LES NŒUDS EXISTANTS
// (avant d'ajouter le nouveau nœud, pour éviter l'explosion)
int randomIndex = rand.nextInt(currentNodeCount);
Node v = g.getNode("n" + randomIndex);
if (v == null) {
v = g.getNode("n0"); // Solution de repli
}
String newNodeId = "n" + currentNodeCount;
g.addNode(newNodeId);
Node newNode = g.getNode(newNodeId);
// Connexion aux voisins de v avec probabilité p
List<Node> neighborsOfV = new ArrayList<>();
for (Edge e : v.edges().toArray(Edge[]::new)) {
Node neighbor = e.getOpposite(v);
if (neighbor != null && !neighbor.getId().equals(newNodeId)) {
neighborsOfV.add(neighbor);
}
}
for (Node neighbor : neighborsOfV) {
if (rand.nextDouble() < p) {
String edgeId = "e_" + currentNodeCount + "_" + neighbor.getId();
if (!g.getNode(newNodeId).hasEdgeBetween(neighbor.getId())) {
g.addEdge(edgeId, newNodeId, neighbor.getId());
}
}
}
// Connexion systématique à v
String edgeToV = "e_" + currentNodeCount + "_to_" + v.getId();
if (!g.getNode(newNodeId).hasEdgeBetween(v.getId())) {
g.addEdge(edgeToV, newNodeId, v.getId());
}
currentNodeCount++;
if (currentNodeCount % 10000 == 0) {
ResultsPrinter.printInfo(
String.format(" Progression: %,d/%,d nœuds générés", currentNodeCount, targetNodes));
}
}
ResultsPrinter.printSuccess(
String.format("Réseau par copie généré: %,d nœuds, %,d arêtes",
g.getNodeCount(), g.getEdgeCount()));
return g;
}
/**
* Effectue l'analyse complète de la Question 7 (Bonus).
*
* Cette méthode teste différentes valeurs du paramètre p, génère les
* réseaux correspondants, et compare leur clustering avec DBLP et
* Barabási-Albert pour démontrer l'amélioration apportée par le
* mécanisme de copie.
*
* @param dblpGraph Le graphe DBLP de référence
* @param baGraph Le graphe Barabási-Albert pour comparaison
*/
public static void analyze(Graph dblpGraph, Graph baGraph) {
long startTime = System.currentTimeMillis();
ResultsPrinter.printHeader("QUESTION 7 (BONUS): Générateur par copie");
ResultsPrinter.printInfo("Objectif: Améliorer le coefficient de clustering par rapport à Barabási-Albert");
// Test de différentes valeurs de p
double[] pValues = {0.3, 0.5, 0.7};
int generatedSize = Math.min(dblpGraph.getNodeCount(), 50000);
ResultsPrinter.printSubHeader("Tests avec différentes valeurs de p");
Graph bestGraph = null;
double bestP = 0.0;
double bestClustering = 0.0;
for (double p : pValues) {
ResultsPrinter.printInfo(String.format("\n=== Test avec p = %.1f ===", p));
Graph copyGraph = generateGraph(generatedSize, p);
double clustering = BasicMetrics.getClusteringCoefficient(copyGraph);
double avgDegree = BasicMetrics.getAverageDegree(copyGraph);
ResultsPrinter.printMetric("Degré moyen", avgDegree);
ResultsPrinter.printMetric("Coefficient de clustering", clustering);
if (clustering > bestClustering) {
bestClustering = clustering;
bestP = p;
bestGraph = copyGraph;
}
}
// Analyse détaillée de la meilleure configuration
ResultsPrinter.printSubHeader("Meilleure configuration: p = " + bestP);
BasicMetrics.analyze(bestGraph, "Copie (p=" + bestP + ")");
DegreeAnalyzer.analyze(bestGraph, "Copie", "copy");
// Tableau de comparaison
ResultsPrinter.printHeader("COMPARAISON CLUSTERING");
double dblpClustering = BasicMetrics.getClusteringCoefficient(dblpGraph);
double baClustering = BasicMetrics.getClusteringCoefficient(baGraph);
ResultsPrinter.printComparisonHeader("DBLP", "Barabási-Albert", "Copie (p=" + bestP + ")");
ResultsPrinter.printComparisonRow("Clustering",
dblpClustering,
baClustering,
bestClustering);
ResultsPrinter.printSeparator();
double improvementVsBA = bestClustering / baClustering;
ResultsPrinter.printMetric("Amélioration vs BA", improvementVsBA + "x");
if (bestClustering > baClustering * 10) {
ResultsPrinter.printSuccess(
"Le générateur par copie reproduit BEAUCOUP MIEUX le clustering!");
} else if (bestClustering > baClustering) {
ResultsPrinter.printSuccess(
"Le générateur par copie améliore le clustering!");
}
ResultsPrinter.printInfo("\nCONCLUSIONS:");
ResultsPrinter.printInfo(" - La méthode de copie crée naturellement des triangles (clustering)");
ResultsPrinter.printInfo(" - Compromis à trouver avec la valeur de p");
ResultsPrinter.printInfo(" - Meilleure modélisation que BA pour les réseaux sociaux!");
ResultsPrinter.printElapsedTime(startTime);
}
}