graphes_matrices.py
Note de lecture générée automatiquement
Le vrai fichier est
scripts/maths/graphes_matrices.py(Obsidian n’affiche pas les scripts eux-mêmes). Ne modifie pas cette note : modifie le script, puis relancepython3 generer_vues_obsidian.pydans le dossierscripts.
- À quoi il sert : M^k, circuits, fermeture transitive, niveaux
- Lancer (dans le dossier
scripts/maths/) :python3 graphes_matrices.py "A>B A>C B>D" 3 - Fiche : M07 - Graphes et plus court chemin
- Retour : 00 - Tous les scripts
Code
#!/usr/bin/env python3
"""
graphes_matrices.py — Graphe orienté : matrice d'adjacence, puissances M^k
(nombre de chemins de longueur k), circuits, fermeture transitive, niveaux.
Utilisation :
python3 graphes_matrices.py "A>B A>C B>D D>A D>E E>D"
python3 graphes_matrices.py "A>B A>C B>D C>E D>E D>F E>F" 3 (puissances jusqu'à M^3)
Chaque arc s'écrit Origine>Destination, séparés par des espaces.
"""
import sys
def produit(m1: list, m2: list) -> list:
taille = len(m1)
return [[sum(m1[i][k] * m2[k][j] for k in range(taille)) for j in range(taille)]
for i in range(taille)]
def ou_booleen(m1: list, m2: list) -> list:
return [[1 if (a or b) else 0 for a, b in zip(l1, l2)] for l1, l2 in zip(m1, m2)]
def afficher(nom: str, matrice: list, sommets: list) -> None:
print(f"\n{nom} :")
print(" " + " ".join(f"{s:>2}" for s in sommets))
for sommet, ligne in zip(sommets, matrice):
print(f"{sommet:>3} [" + " ".join(f"{v:>2}" for v in ligne) + " ]")
def niveaux(sommets: list, arcs: list) -> dict:
# Méthode des niveaux : on retire successivement les sommets sans prédécesseur
restants = set(sommets)
niveau_de = {}
numero = 0
while restants:
sans_pred = [s for s in sorted(restants)
if not any(o in restants and d == s for o, d in arcs)]
if not sans_pred:
print("\nLe graphe contient un circuit : pas de niveaux possibles.")
return {}
for s in sans_pred:
niveau_de[s] = numero
restants -= set(sans_pred)
numero += 1
return niveau_de
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit(__doc__)
arcs = [tuple(arc.split(">")) for arc in sys.argv[1].split()]
puissance_max = int(sys.argv[2]) if len(sys.argv) > 2 else 3
sommets = sorted(set(s for arc in arcs for s in arc))
index = {s: i for i, s in enumerate(sommets)}
n = len(sommets)
adjacence = [[0] * n for _ in range(n)]
for origine, destination in arcs:
adjacence[index[origine]][index[destination]] = 1
afficher("Matrice d'adjacence M (ligne = origine, colonne = destination)", adjacence, sommets)
for s in sommets:
succ = [d for o, d in arcs if o == s]
pred = [o for o, d in arcs if d == s]
print(f" {s} : successeurs {succ or '∅'}, prédécesseurs {pred or '∅'}")
puissance = adjacence
for k in range(2, puissance_max + 1):
puissance = produit(puissance, adjacence)
afficher(f"M^{k} (nombre de chemins de longueur {k})", puissance, sommets)
print(f" Circuits de longueur {k} (somme de la diagonale) : {sum(puissance[i][i] for i in range(n))}")
# Fermeture transitive : M ∨ M^2 ∨ … ∨ M^n (en booléen)
fermeture = adjacence
courante = adjacence
for _ in range(2, n + 1):
courante = [[1 if v else 0 for v in ligne] for ligne in produit(courante, adjacence)]
fermeture = ou_booleen(fermeture, courante)
afficher("Fermeture transitive M̂ (1 = il existe un chemin)", fermeture, sommets)
if any(fermeture[i][i] for i in range(n)):
print(" => 1 sur la diagonale : le graphe contient AU MOINS UN CIRCUIT.")
else:
print(" => Diagonale nulle : AUCUN circuit.")
print(f" Niveaux : {niveaux(sommets, arcs)}")