Graphes et algorithmes - TP 2 - CORRIGE

1. Ecrire un programme qui lit un fichier de points, et construit un graphe antisymétrique sans boucle tel que :
  fd = fopen(argv[1], "r"); // ouverture fichier
  if (!fd)
  {
    fprintf(stderr, "cannot open file: %s\n", argv[1]);
    exit(0);
  }
  fscanf(fd, "%d", &ns);    // lit le nombre de sommets dans le fichier
  na = (ns * (ns - 1)) / 2; // calcule le nombre d'arcs
  g = InitGraphe(ns, na);
  for (i = 0; i < ns; i++)
    fscanf(fd, "%lf %lf", &(g->x[i]), &(g->y[i]));
  fclose(fd);

  // construit le graphe des distances sur les sommets de g (antisymetrique et sans boucle)
  narc = 0;
  for (i = 0; i < ns; i++)
    for (j = i+1; j < ns; j++)
    {
      AjouteArc(g, i, j);
      g->tete[narc] = i;
      g->queue[narc] = j;
      g->v_arcs[narc] = -DistanceEuclidienne(g->x[i], g->y[i], g->x[j], g->y[j]);
      narc++;
    }
Visualiser les graphes obtenus pour différentes valeurs de d. Peut-on trouver une valeur qui permette d'isoler les ``points de bruit'' des ``trajectoires de particules'', dans les trois images bs0, bs1, bs2 ?

Non.


2. Quelle est (approximativement) la valeur minimale dmin de d telle que le graphe obtenu à partir de bs0.list soit connexe ?

Proposez (sans l'implémenter) une méthode pour trouver la valeur exacte de dmin pour un ensemble quelconque de points.

Partir du graphe vide sur N sommets, ajouter les arcs par poids croissant jusqu'à ce que le graphe obtenu soit connexe.

Complexité de la méthode ?

Tri en O(MlogM), avec M = nombre d'arcs = N * (N - 1) / 2, test connexité en O(n+m) pour n sommets et m arcs, d'où au pire O(MlogM + N+1+N+2+...+N+M) = O(MlogM + NM + M2) = O(M2)


3. On suppose que les trajectoires se coupent entre elles et ne forment pas de cycle. Calculez sur le graphe valué par les distances un arbre de poids minimum. Pour cela, il faut implémenter un des algorithmes étudiés en cours (Kruskal 1, 2). Les fonctions de tri, test d'existence d'un cycle et test de connexité sont disponibles, ainsi que les fonctions de base pour ajouter ou retirer un arc, initialiser un nouveau graphe, etc.


/* ====================================================================== */
graphe * Kruskal1(graphe * g, graphe *g_1)
/* ====================================================================== */
{
  int n = g->nsom;
  int m = g->narc;
  graphe * apm;    /* pour le resultat */
  graphe * apm_1;  /* pour la detection de cycles */
  int *A;          /* tableau pour ranger les index des arcs */
  int i, j, t, q;
  boolean *Ct = EnsembleVide(n);

  /* tri des arcs par poids croissant */
  A = (int *)malloc(m * sizeof(int)); /* allocation index */
  if (A == NULL)
  {   fprintf(stderr, "Kruskal1 : malloc failed\n");
      exit(0);
  }  
  for (i = 0; i < m; i++) A[i] = i; /* indexation initiale */
  TriRapideStochastique(A, g->v_arcs, 0, m-1);

  /* construit le graphe resultat, initialement vide */
  apm = InitGraphe(n, n-1);
  apm_1 = InitGraphe(n, n-1);

  /* Boucle sur les arcs pris par ordre decroissant:
       on ajoute un arc dans apm, si cela ne cree pas un cycle dans apm.
     Arret lorsque nb arcs ajoutes = n-1. */

  j = 0; 
  i = m - 1;
  while (j < n-1)
  {
    t = g->tete[A[i]]; q = g->queue[A[i]]; 
    CompConnexe(apm, apm_1, t, Ct);
    if (!Ct[q]) 
    {
      AjouteArc(apm, t, q);
      AjouteArc(apm_1, q, t);
      j++; 
    }
    i--;
    if (i < 0)
    {   fprintf(stderr, "Kruskal1 : graphe d'origine non connexe\n");
        exit(0);
    }  
  }

  if (g->x != NULL) // recopie les coordonnees des sommets pour l'affichage
    for (i = 0; i < n; i++) { apm->x[i] = g->x[i];  apm->y[i] = g->y[i]; }

  free(A);
  free(Ct);
  TermineGraphe(apm_1);
  return apm;
} /* Kruskal1() */
Commentez les résultats obtenus sur les trois images bs0, bs1, bs2, en particulier : quels sont les défauts de la méthode ? Illustrez ces défauts par des exemples précis.

Deux types de défauts fréquents : les points de bruit qui créent des "branches", et ceux qui génèrent des déviations dans les trajectoires. Egalement, des déviations peuvent être constatées près des croisements de trajectoires, et de mauvaises connexions peuvent être effectuées entre deux trajectoires proches l'une de l'autre.


4. On voit sur certaines configurations qu'il est nécessaire de tenir compte du fait que des points appartenant à une même trajectoire sont approximativement alignés.

On se propose de redéfinir la nature des poids associés aux arcs, de façon à favoriser la sélection des arcs qui non seulement sont courts, mais sont également alignés avec des arcs adjacents. De plus, cette définition devra être relativement simple à implémenter.

Une idée qui "marche" : on considère le segment (i,j), avec i et j dans E, l'ensemble des points à relier. Soit i' le symétrique de i par rapport à j, et j' le symétrique de j par rapport à i. On peut prendre :

p = d(i,j) + alpha * [ min {d(i',k), k in E \ {i,j} } + min {d(j',k), k in E \ {i,j} } ]

Le point i' (idem j') peut être interprété comme une "prédiction" de l'emplacement d'un prochain point de trajectoire, si le segment (i,j) fait effectivement partie d'une trajectoire.

Le paramètre alpha permet de donner plus ou moins d'importance à la distance ou à la linéarité des trajectoires. Typiquement, alpha = 1.


5. Proposez et implémentez une stratégie pour éliminer les points de bruit, c'est-à-dire les points qui ne font clairement partie d'aucune trajectoire.

Une branche est une partie de l'arbre située entre un point extrémité (un seul voisin) et un point multiple (ayant 3 voisins ou plus). On pourra éliminer les branches consituées de moins de K points, avec K entre 2 et 5 typiquement.