{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Séance 2 — Parcours & distances (BFS / DFS)\n",
        "\n",
        "## Objectifs pédagogiques\n",
        "- Comprendre les notions de **chemin**, **distance** et ** composantes connexes**.\n",
        "- Distinguer **BFS** (parcours en largeur) et **DFS** (parcours en profondeur).\n",
        "- Utiliser `networkx` pour calculer des plus courts chemins dans un graphe **non pondéré**.\n",
        "- Relier les distances à des interprétations sociologiques (proximité, relais, \"six degrés\").\n",
        "\n",
        "## Rappels\n",
        "- Dans un **graphe non pondéré**, un **plus court chemin** est le chemin avec le **moins d'arêtes**.\n",
        "- **BFS** explore par **couches** (distance croissante) et permet de trouver un plus court chemin.\n",
        "- **DFS** explore en **profondeur** (par branches) ; utile pour détecter des cycles/composantes mais **ne garantit pas** un plus court chemin.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# %pip install networkx matplotlib\n",
        "\n",
        "import networkx as nx\n",
        "import matplotlib.pyplot as plt\n",
        "from collections import deque"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1) Graphe de départ : petit réseau social"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Vous pouvez modifier/étendre ce graphe\n",
        "G = nx.Graph()\n",
        "G.add_edges_from([\n",
        "    (\"Alice\",\"Bob\"),\n",
        "    (\"Bob\",\"Claire\"),\n",
        "    (\"Claire\",\"Emma\"),\n",
        "    (\"Alice\",\"David\"),\n",
        "    (\"David\",\"Emma\"),\n",
        "    (\"Emma\",\"Fanny\"),\n",
        "    (\"Bob\",\"Gaston\"),\n",
        "])\n",
        "\n",
        "# Visualisation simple\n",
        "plt.figure()\n",
        "nx.draw(G, with_labels=True)\n",
        "plt.show()\n",
        "\n",
        "print(\"Nœuds :\", list(G.nodes()))\n",
        "print(\"Arêtes:\", list(G.edges()))\n",
        "print(\"Composantes connexes:\", nx.number_connected_components(G))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "**Question :** Y a-t-il un unique groupe connecté, ou plusieurs ? Impact sociologique : des individus isolés auront des distances infinies vers le reste."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2) Plus courts chemins et distances avec NetworkX"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "source, cible = \"Alice\", \"Fanny\"\n",
        "chemin = nx.shortest_path(G, source=source, target=cible)  # plus court chemin (non pondéré)\n",
        "dist = nx.shortest_path_length(G, source=source, target=cible)\n",
        "print(f\"Plus court chemin de {source} à {cible} :\", chemin)\n",
        "print(f\"Distance (nombre d'arêtes) :\", dist)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "**Exercice 2.1** : testez 3 autres paires (par ex. `('Gaston','Fanny')`, `('Alice','Claire')`, `('David','Bob')`).\n",
        "\n",
        "**Exercice 2.2** : si vous supprimez l'arête `('David','Emma')`, que devient la distance `Alice→Fanny` ?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3) Implémenter un BFS pédagogique (pour comprendre l'ordre d'exploration)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def bfs_layers(graph, start):\n",
        "    \"\"\"Retourne l'ordre de visite et les couches de distance (dict: noeud -> distance)\n",
        "    BFS avec file d'attente (deque).\n",
        "    \"\"\"\n",
        "    visited = set([start])\n",
        "    dist = {start: 0}\n",
        "    order = []\n",
        "    q = deque([start])\n",
        "\n",
        "    while q:\n",
        "        u = q.popleft()\n",
        "        order.append(u)\n",
        "        for v in graph.neighbors(u):\n",
        "            if v not in visited:\n",
        "                visited.add(v)\n",
        "                dist[v] = dist[u] + 1\n",
        "                q.append(v)\n",
        "    return order, dist\n",
        "\n",
        "order, dist = bfs_layers(G, \"Alice\")\n",
        "print(\"Ordre BFS depuis Alice:\", order)\n",
        "print(\"Distances depuis Alice:\", dist)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "**Exercice 3.1** : Comparez les **distances** renvoyées par `bfs_layers` et `nx.shortest_path_length` pour 3 nœuds.\n",
        "\n",
        "**Exercice 3.2** : Quel nœud est le plus proche (distance minimale) d'**Alice** ? Le plus lointain ?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4) Implémenter un DFS (ordre d'exploration par branches)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def dfs_order(graph, start, visited=None, order=None):\n",
        "    if visited is None: visited = set()\n",
        "    if order is None: order = []\n",
        "    visited.add(start)\n",
        "    order.append(start)\n",
        "    for v in graph.neighbors(start):\n",
        "        if v not in visited:\n",
        "            dfs_order(graph, v, visited, order)\n",
        "    return order\n",
        "\n",
        "print(\"Ordre DFS depuis Alice:\", dfs_order(G, \"Alice\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "**Exercice 4.1** : Pourquoi **l'ordre DFS** diffère-t-il souvent de l'ordre BFS ?\n",
        "\n",
        "**Exercice 4.2** : DFS trouve-t-il un plus court chemin ? Expliquez avec un contre-exemple si possible."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5) Distances globales : diamètre & distance moyenne (si connexe)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "if nx.is_connected(G):\n",
        "    # Diamètre = plus longue distance entre deux nœuds\n",
        "    d = nx.diameter(G)\n",
        "    # Distance moyenne (longueur moyenne des plus courts chemins)\n",
        "    apl = nx.average_shortest_path_length(G)\n",
        "    print(\"Diamètre:\", d)\n",
        "    print(\"Distance moyenne:\", apl)\n",
        "else:\n",
        "    print(\"Le graphe n'est pas connexe : diamètre et distance moyenne ne sont pas définis globalement.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "**Exercice 5.1** : Ajoutez/supprimez une arête et observez l'effet sur la distance moyenne. Interprétez sociologiquement (ex. apparition d'un **pont** qui réduit les distances)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 6) Étude guidée : \"six degrés de séparation\" (mini-expérience)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Construisons un graphe en anneau + quelques liens longue portée pour réduire drastiquement les distances\n",
        "H = nx.cycle_graph(12)  # 12 individus en cercle (0..11)\n",
        "H = nx.relabel_nodes(H, {i: f\"P{i}\" for i in range(12)})\n",
        "H.add_edge(\"P0\",\"P6\")   # raccourci longue portée\n",
        "H.add_edge(\"P3\",\"P9\")   # autre raccourci\n",
        "\n",
        "plt.figure()\n",
        "nx.draw(H, with_labels=True)\n",
        "plt.show()\n",
        "\n",
        "print(\"Connexe:\", nx.is_connected(H))\n",
        "print(\"Distance moyenne:\", nx.average_shortest_path_length(H))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "**Questions**\n",
        "1. Que se passe-t-il si on **retire** les liens longue portée ?\n",
        "2. Pourquoi quelques liens inter-groupes peuvent-ils **réduire fortement** les distances ?\n",
        "3. Donnez une interprétation en termes de diffusion d'une rumeur/idée."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 7) Synthèse écrite\n",
        "- Comparez BFS et DFS sur votre graphe.\n",
        "- Donnez 2 exemples de distances pertinentes en sociologie (ex. accès à l'information, recrutement).\n",
        "- Proposez une **hypothèse** : l'ajout d'un lien entre deux groupes réduira X ; comment le tester ?"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75d566ac",
      "metadata": {},
      "source": []
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.x"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
