Matrices d'un graphe et relations binaires¶

Remarque : nous aurons besoin d'importer graph, numpy, et certaines fonctions de votre TP1.

In [45]:
import numpy as np
from graph import *

1 Matrice d'adjacence¶

1.1 Définition¶

Tous les graphes manipulés dans ce TP et les suivants seront, sauf mention du contraire, des 1-graphes.

In [46]:
# soit le graphe g0 suivant :
g0 = Graph()
g0.successors = {0:{2,3},
                 1:{1}, 
                 2:{1,3}, 
                 3:{0}}
g0.display(title="G0")
Out[46]:
G0 G0 0 0 2 2 0->2 3 3 0->3 1 1 1->1 2->1 2->3 3->0

La matrice d'adjacence M d'un graphe orienté G est définie par :

$M_{i,j}$ = 1 si il y a un arc allant du sommet i vers le sommet j
$M_{i,j}$ = 0 sinon

Pour un graphe non orienté, la définition est similaire (on remarque donc que la matricie d'adjacence d'un graphe non-orienté est symétrique).

Exemple : Une matrice d'adjacence M sera un objet array du package numpy.

In [47]:
# soit M0 la matrice du graphe g1 précédent
M0 = np.array([[0,0,1,1],
               [0,1,0,0],
               [0,1,0,1],
               [1,0,0,0]])
print(M0)
[[0 0 1 1]
 [0 1 0 0]
 [0 1 0 1]
 [1 0 0 0]]

1.2 passage matrice/graphe¶

  • Créer une fonction graphe_vers_matrice(g) qui retourne la matrice d'adjacence d'un graphe g (orienté ou non) donné.

Exemple :

In [49]:
graphe_vers_matrice(g0)
Out[49]:
array([[0., 0., 1., 1.],
       [0., 1., 0., 0.],
       [0., 1., 0., 1.],
       [1., 0., 0., 0.]])
  • Créer une fonction matrice_vers_graphe(M,orientation) qui retourne le graphe (orienté si le paramètre orientation vaut True, non orienté sinon) d'une matrice d'adjacence M.

Exemple :

In [51]:
test = matrice_vers_graphe(M0, True)
test.successors
Out[51]:
{0: {2, 3}, 1: {1}, 2: {1, 3}, 3: {0}}

2. Propriétés des relations binaires¶

2.1 Construction des matrices test¶

  • Construire les matrices suivantes :
In [52]:
M1 = np.array([[1,1,0],[1,1,0],[0,0,1]])
M2 = np.array([[1,1,1],[1,0,0],[0,0,1]])
M3 = np.array([[1,1,1],[0,1,1],[0,0,1]])
M4 = np.array([[1,0,1],[0,0,0],[0,0,0]])
In [53]:
print(M1)
[[1 1 0]
 [1 1 0]
 [0 0 1]]
In [54]:
print(M2)
[[1 1 1]
 [1 0 0]
 [0 0 1]]
In [55]:
print(M3)
[[1 1 1]
 [0 1 1]
 [0 0 1]]
In [56]:
print(M4)
[[1 0 1]
 [0 0 0]
 [0 0 0]]

2.2 Réflexivité¶

  • Créer une fonction reflexive(M) qui, pour une matrice booléene donnée $M$, renvoie Truesi la relation binaire associée est réflexive, False sinon.

  • Tester votre fonction sur les matrices $M_i$ précédemment construites.

Exemple :

In [58]:
print(reflexive(M1),reflexive(M2),reflexive(M3),reflexive(M4))
True False True False

2.3 Symétrie et antisymétrie¶

  • Mêmes questions que précédemment, avec deux fonctions symetrique(M) et antisymetrique(M).

Exemple :

In [60]:
print(symetrique(M1),symetrique(M2),symetrique(M3),symetrique(M4))
True False False False
In [62]:
print(antisymetrique(M1),antisymetrique(M2),antisymetrique(M3),antisymetrique(M4))
False False True True

2.4 Transitivité¶

Rappel :
Un graphe orienté $G$ est dit transitif si la propriété suivante est vérifiée : $$\forall {i,j,k}, \quad G_{i,j}=1 \text{ et } G_{j,k}=1 \quad \Rightarrow \quad G_{i,k}=1$$

  • Tracer les graphes correspondant aux matrices $M_i$. Vérifier visuellement la transitivité ou non des graphes.
In [63]:
g1 = matrice_vers_graphe(M1, True)
g1.display(title="G1")
Out[63]:
G1 G1 0 0 0->0 1 1 0->1 1->0 1->1 2 2 2->2
In [64]:
g2 = matrice_vers_graphe(M2, True)
g2.display(title="G2")
Out[64]:
G2 G2 0 0 0->0 1 1 0->1 2 2 0->2 1->0 2->2
In [65]:
g3 = matrice_vers_graphe(M3, True)
g3.display(title="G3")
Out[65]:
G3 G3 0 0 0->0 1 1 0->1 2 2 0->2 1->1 1->2 2->2
In [66]:
g4 = matrice_vers_graphe(M4, True)
g4.display(title="G4")
Out[66]:
G4 G4 0 0 0->0 2 2 0->2 1 1

Créer une fonction transitive(g) en utilisant la définition de la transitivité.

Exemple :

In [68]:
print(transitive(g1),transitive(g2),transitive(g3),transitive(g4))
True False True True

Point de vue matriciel¶

On rappelle qu'une relation binaire est transitive ssi sa matrice $M$ vérifie : $M = M+M^2$

  • Créer une fonction transitive2(M), qui utilise la propriété précédente, et tester la sur les matrices $M_i$.

Attention aux opérations avec des matrices booléennes !
Une possibilté est de faire les opérations matricielles classiques puis de revenir à du booléen, en remplaçant tous les coefficients strictement positifs par 1.

Exemple :

In [70]:
print(transitive(M1),transitive(M2),transitive(M3),transitive(M4))
True False True True

3. Relation binaire particulière¶

3.1 Relation d'équivalence¶

  • Créer une fonction equivalence(M) qui, pour une matrice booléene donnée $M$, renvoie Truesi la relation binaire associée est une relation d'equivalence, False sinon.

Exemple :

In [72]:
print(equivalence(M1),equivalence(M2),equivalence(M3))
True False False

3.2 Relation d'ordre¶

  • Créer une fonction ordre(M) qui, pour une matrice booléene donnée $M$, renvoie Truesi la relation binaire associée est une relation d'ordre, False sinon.

Exemple :

In [74]:
print(ordre(M1),ordre(M2),ordre(M3))
False False True

3.3 Diagramme de Hasse 🌶️¶

Le diagramme de Hasse d'une relation d'ordre est obtenu en retirant toutes les boucles issues de la réflexivité, ainsi que tous les raccourcis issus de la transitivité. C'est en quelque sorte une version plus lisible de la structure ordonnée.

  • Créer une fonction hasse(M) qui, pour une matrice booléene donnée $M$ d'une relation d'ordre, renvoie la matrice du diagramme de Hasse associé à $M$.

Exemple :

In [76]:
M4=np.zeros((4,4))
M4[0,0]=M4[0,2]=M4[0,3]=1
M4[1,0]=M4[1,1]=M4[1,2]=M4[1,3]=1
M4[2,2]=M4[2,3]=1
M4[3,3]=1
print(M4, ordre(M4))
[[1. 0. 1. 1.]
 [1. 1. 1. 1.]
 [0. 0. 1. 1.]
 [0. 0. 0. 1.]] True
In [77]:
g4 = matrice_vers_graphe(M4,True)
g4.display(title='G4')
Out[77]:
G4 G4 0 0 0->0 2 2 0->2 3 3 0->3 1 1 1->0 1->1 1->2 1->3 2->2 2->3 3->3
In [78]:
g4Hasse = matrice_vers_graphe(hasse(M4),True)
g4Hasse.display(title='G4Hasse')
Out[78]:
G4Hasse G4Hasse 0 0 2 2 0->2 1 1 1->0 3 3 2->3
  • Créer une fonction matdiv(n) retournant la matrice d'adjacence de la relation "divise" sur les nombres entiers 1, 2, ..., n.

On aura donc $M_{i,j}$ = 1 ssi (i+1) divise (j+1).

Exemple :

In [80]:
matdiv(8)
Out[80]:
array([[1, 1, 1, 1, 1, 1, 1, 1],
       [0, 1, 0, 1, 0, 1, 0, 1],
       [0, 0, 1, 0, 0, 1, 0, 0],
       [0, 0, 0, 1, 0, 0, 0, 1],
       [0, 0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 0, 0, 1]])
  • Afficher le diagramme de Hasse de la relation "divise" sur 1, 2, ..., 8.
In [81]:
g5 = matrice_vers_graphe(hasse(matdiv(8)),True)
g5.display(label=[1,2,3,4,5,6,7,8], title="matdiv(8)")
Out[81]:
matdiv(8) matdiv(8) 1 1 2 2 1->2 3 3 1->3 5 5 1->5 7 7 1->7 4 4 2->4 6 6 2->6 3->6 8 8 4->8

4 Bonus¶

Surf Aléatoire¶

Soit un mini-site intranet S composé de 5 pages, liées entre elles de la façon suivante :

In [83]:
s.display(title="S")
Out[83]:
S S 0 0 1 1 0->1 3 3 0->3 4 4 0->4 1->3 2 2 2->1 2->3 3->1 3->2 3->4 4->0

Supposons un surfeur aléatoire se trouvant au sommet 0, et se déplaçant un certain nombre de fois sur ce site de manière équiprobable. On cherche alors à déterminer les probabilités de se trouver sur chacune des pages.

Pour cela, notons $P_n$ la matrice ligne contenant les probabilités de se trouver à chaque sommet après n déplacements.
Par exemple, la matrice des probabilités initiales est donc $P_0$ = (1, 0, 0, 0,0). En effet, le surf aléatoire commence au sommet 0.
Après 1 déplacement aléatoire, la matrice des probabilités est $P_1$ = (0, 1/3, 0, 1/3, 1/3). En effet, il y a une chance sur 3 d'être en 1, en 3 ou en 4.

On définit de plus la matrice de transition $Mt$ par :$Mt_{i,j}$ = "probabilité de passer du sommet i au sommet j"

On pourrait alors montrer que les probabilités de se trouver aux différents sommets après $n$ déplacements sont données par la formule :
$P_n$ = $P_{0}\times(Mt)^n$

  • Construire le graphe S ci-dessus.

  • Construire une fonction transition(g) qui calcule, pour un graphe $g$ donné, sa matrice de transition $Mt$.

Exemple :

In [85]:
transition(s)
Out[85]:
array([[0.        , 0.33333333, 0.        , 0.33333333, 0.33333333],
       [0.        , 0.        , 0.        , 1.        , 0.        ],
       [0.        , 0.5       , 0.        , 0.5       , 0.        ],
       [0.        , 0.33333333, 0.33333333, 0.        , 0.33333333],
       [1.        , 0.        , 0.        , 0.        , 0.        ]])
  • Calculer les probabilités de se trouver aux différents sommets de S après 1 déplacement.
  • Même question, mais après 10 déplacements. (on pourra utiliser la fonction codée au semestre 1 puissance(A,p) qui calcule $A^p$)
  • Même question, après 20 déplacements, 50 déplacements, 100 déplacements. Que constatez-vous ?