Editez le fichier exo121.c, lisez-le, compilez et exécutez. Analyser la fonction Sym. Pourquoi n'est-elle pas efficace ?
Les deux boucles "for" imbriquées parcourent tous les arcs possibles du graphe complet sur n sommets, même s'ils ne sont pas présents dans notre graphe. De plus, le test EstSuccesseur(g, i, j) demande, au pire, un parcours complet de la liste des successeurs du sommet i.
Quelle est la complexité de calcul de l'algorithme ?
O(n(n+m))
Editez le fichier exo122.c, lisez-le, compilez et exécutez. Notez les temps de calculs obtenus pour diverses tailles de graphes.
Exemple:
couprie@linux:~/Graphes > ./exo122.exe 10 25 temps de traitement : 1.6e-05 secondes couprie@linux:~/Graphes > ./exo122.exe 100 250 temps de traitement : 0.000847 secondes couprie@linux:~/Graphes > ./exo122.exe 1000 2500 temps de traitement : 0.083336 secondes couprie@linux:~/Graphes > ./exo122.exe 10000 25000 temps de traitement : 8.03387 secondes couprie@linux:~/Graphes > ./exo122.exe 100000 250000 temps de traitement : 803.758 secondes
Dans le fichier exo122.c, modifiez la fonction Sym pour
rendre l'algorithme linéaire.
/* ====================================================================== */
graphe * Symetrique(graphe * g)
/* ====================================================================== */
{
graphe *g_1;
int nsom, narc, al_arcs, k, i, j;
pcell p;
nsom = g->nsom;
narc = g->narc;
g_1 = InitGraphe(nsom, narc);
for (i = 0; i < nsom; i++) /* pour tout i sommet de g */
{
for (p = g->gamma[i]; p != NULL; p = p->next)
{ /* pour tout j successeur de i */
j = p->som;
AjouteArc(g_1, j, i);
}
}
return g_1;
} /* Symetrique() */
Refaire les mesures de temps de calcul avec la nouvelle version, et comparer.
couprie@linux:~/Graphes > ./exo123.exe 10 25
temps de traitement : 8e-06 secondes
couprie@linux:~/Graphes > ./exo123.exe 100 250
temps de traitement : 4.8e-05 secondes
couprie@linux:~/Graphes > ./exo123.exe 1000 2500
temps de traitement : 0.000395 secondes
couprie@linux:~/Graphes > ./exo123.exe 10000 25000
temps de traitement : 0.006711 secondes
couprie@linux:~/Graphes > ./exo123.exe 100000 250000
temps de traitement : 0.112156 secondes
couprie@linux:~/Graphes > ./exo123.exe 1000000 2500000
temps de traitement : 1.26103 secondes
Formulez ce problème en termes de graphe et donnez une solution pour ce cas particulier.
On trace le graphe des "inimitiés" (à gauche):
Le problème a une solution si ce graphe est biparti (voir ci-dessous). On trouve facilement ici une solution, qui consiste en un bi-coloriage du graphe (à droite). Cet exemple provient du site suivant : http://campus.northpark.edu/wicksBook/GraphTheory/Bipartite/index.html.
Proposer un algorithme, linéaire en temps de calcul, qui
indique si un graphe G donné est biparti ou pas, et si oui, qui
indique un bi-coloriage de G.
Algo Biparti
Donnees: G = (E, gamma), G_1 = symetrique de G, n = |E|
Resultats: biparti (booleen), couleur (tableau de taille n)
// le graphe est suppose connexe
// on commence le coloriage au sommet 0
C := {0}; T1 := {0}; T2 := {};
coul := 0; couleur[0] := coul;
Tant que T1 non vide faire
coul := (coul + 1) modulo 2
Tant que il existe un sommet i dans T1 faire
T1 := T1 \ {i}
Pour tout s successeur de i dans G ou dans G_1
Si s n'est pas dans C alors
C := C u {s}
T2 := T2 u {s}
couleur[s] := coul
Sinon
Si couleur[s] != coul alors biparti := false; RETURN
fin Sinon
fin Pour
fin Tant que
T1 := T2
T2 := {}
fin Tant que
biparti := true
fin Algo
Evidemment ce n'est qu'une solution parmi d'autres !
/* ====================================================================== */ /*! \fn boolean Biparti(graphe * g, graphe *g_1) \param g (entrée) : un graphe. \param g_1 (entrée) : le graphe symétrique de g. \return TRUE si le graphe g est biparti, FALSE sinon \brief vérifie si le graphe g est biparti, si oui on peut récupérer la bipartition dans g->v_sommets, sous la forme de deux "couleurs" 0 et 1. */ boolean Biparti(graphe * g, graphe *g_1) /* ====================================================================== */ { Lifo * T1; /* liste temporaire geree en pile (Last In, First Out) */ Lifo * T2; /* liste temporaire geree en pile (Last In, First Out) */ Lifo * T; /* pour l'echange */ int i, n, s; pcell p; boolean * C; /* pour eviter de reparcourir un sommet plusieurs fois */ int couleur; n = g->nsom; T1 = CreeLifoVide(n); T2 = CreeLifoVide(n); C = EnsembleVide(n); C[0] = TRUE; LifoPush(T1, 0); /* on part d'un sommet arbitraire (0 en l'occurence) */ couleur = 0; g->v_sommets[0] = (double)couleur; while (!LifoVide(T1)) /* boucle jusqu'a stabilite */ { couleur++; couleur = couleur % 2; while (!LifoVide(T1)) /* boucle pour le parcours d'un niveau */ { i = LifoPop(T1); for (p = g->gamma[i]; p != NULL; p = p->next) { /* pour tout s successeur de i */ s = p->som; if (!C[s]) { C[s] = TRUE; LifoPush(T2, s); g->v_sommets[s] = (double)couleur; } else if (g->v_sommets[s] != (double)couleur) return FALSE; } for (p = g_1->gamma[i]; p != NULL; p = p->next) { /* pour tout s successeur de i dans g_1 */ s = p->som; if (!C[s]) { C[s] = TRUE; LifoPush(T2, s); g->v_sommets[s] = (double)couleur; } else if (g->v_sommets[s] != (double)couleur) return FALSE; } } // fin du niveau T = T2; T2 = T1; T1 = T; } LifoTermine(T1); LifoTermine(T2); free(C); return TRUE; } /* Biparti() */
Montrer qu'un graphe est biparti si et seulement si il ne contient pas de cycle de longueur impaire (à faire hors TP).
/* ====================================================================== */ /*! \fn boolean Biparti(graphe * g, graphe *g_1) \param g (entrée) : un graphe. \param g_1 (entrée) : le graphe symétrique de g. \return TRUE si le graphe g est biparti, FALSE sinon \brief vérifie si le graphe g est biparti, si oui on peut récupérer la bipartition dans g->v_sommets, sous la forme de deux "couleurs" 0 et 1, sinon les valeurs dans g->v_sommets indiquent un cycle de longueur impaire. */ boolean Biparti(graphe * g, graphe *g_1) /* ====================================================================== */ { Lifo * T1; /* liste temporaire geree en pile (Last In, First Out) */ Lifo * T2; /* liste temporaire geree en pile (Last In, First Out) */ Lifo * T; /* pour l'echange */ int i, n, s, t, k; pcell p; boolean * C; /* pour eviter de reparcourir un sommet plusieurs fois */ int couleur; int * back; /* le "fil d'Arianne" (pointeurs arriere) */ n = g->nsom; T1 = CreeLifoVide(n); T2 = CreeLifoVide(n); C = EnsembleVide(n); back = (int *)malloc(n * sizeof(int)); C[0] = TRUE; LifoPush(T1, 0); /* on part d'un sommet arbitraire (0 en l'occurence) */ couleur = 0; g->v_sommets[0] = (double)couleur; while (!LifoVide(T1)) /* boucle jusqu'a stabilite */ { couleur++; couleur = couleur % 2; while (!LifoVide(T1)) /* boucle pour le parcours d'un niveau */ { i = LifoPop(T1); for (p = g->gamma[i]; p != NULL; p = p->next) { /* pour tout s successeur de i */ s = p->som; if (!C[s]) { C[s] = TRUE; LifoPush(T2, s); g->v_sommets[s] = (double)couleur; back[s] = i; } else if (g->v_sommets[s] != (double)couleur) goto NONBIPARTI; } for (p = g_1->gamma[i]; p != NULL; p = p->next) { /* pour tout s successeur de i */ s = p->som; if (!C[s]) { C[s] = TRUE; LifoPush(T2, s); g->v_sommets[s] = (double)couleur; back[s] = i; } else if (g->v_sommets[s] != (double)couleur) goto NONBIPARTI; } } // fin du niveau T = T2; T2 = T1; T1 = T; } LifoTermine(T1); LifoTermine(T2); free(C); free(back); return TRUE; NONBIPARTI: // re-initialisation de T1 et du champ v_sommets LifoFlush(T1); for (k = 0; k < n; k++) g->v_sommets[k] = (double)0; // remonte les pointeurs "back" en partant de s jusqu'au sommet 0 // stocke la valeur -1 dans le champ v_sommets des sommets parcourus (sauf s) t = s; do { t = back[t]; g->v_sommets[t] = (double)(-1); } while (t != 0); // remonte les pointeurs "back" en partant de i jusqu'a un sommet marque "-1" // stocke les sommets dans T1 (y compris le dernier) t = i; while (g->v_sommets[t] != (double)(-1)) { LifoPush(T1, t); t = back[t]; } // while (g->v_sommets[t] != (double)(-1)) LifoPush(T1, t); // remonte les pointeurs "back" en partant de s jusqu'au sommet t (le dernier empile) // numerote les sommets k = 1; do { g->v_sommets[s] = (double)k; k++; s = back[s]; } while (s != t); // retire les sommets de la lifo T1 // numerote les sommets while (!LifoVide(T1)) { s = LifoPop(T1); g->v_sommets[s] = (double)k; k++; } // while (!LifoVide(T1)) LifoTermine(T1); LifoTermine(T2); free(C); free(back); return FALSE; } /* Biparti() */