Add university content

This commit is contained in:
2025-09-26 11:16:23 +02:00
parent 45054aef03
commit 76bbd2e5ad
125 changed files with 230 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
graphe = {
"node1": ["node2", "node4"],
"node2": ["node2", "node3", "node6"],
"node3": ["node5"],
"node5": ["node5", "node6"],
"node4": ["node1", "node5"],
"node6": [],
}
print(graphe["node1"])
def bfs(graphe, start_node):
queue = [start_node]
visited = set()
while queue:
node = queue.pop(0)
if node not in visited:
print(node)
visited.add(node)
for neighbor in graphe[node]:
if neighbor not in visited:
queue.append(neighbor)
bfs(graphe, "node1")

Binary file not shown.

View File

@@ -0,0 +1,57 @@
# Chatgpt code
import heapq
def dijkstra(graph, source):
distances = {node: float("inf") for node in graph}
distances[source] = 0
previous = {node: None for node in graph}
queue = [(0, source)]
while queue:
current_distance, current_node = heapq.heappop(queue)
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node]:
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
previous[neighbor] = current_node
heapq.heappush(queue, (distance, neighbor))
return distances, previous
def reconstruct_path(previous, start, end):
path = []
node = end
while node is not None:
path.append(node)
node = previous[node]
path.reverse()
return path
if __name__ == "__main__":
graph = {
"R1": [("R2", 1)],
"R2": [("R3", 1), ("R5", 10)],
"R3": [("R4", 1), ("R5", 1)],
"R5": [("R4", 10)],
"R4": [],
}
source = "R1"
destination = "R4"
distances, previous = dijkstra(graph, source)
print("Shortest distances from", source, ":", distances)
path = reconstruct_path(previous, source, destination)
print("Shortest path from", source, "to", destination, ":", path)

0
high-school/graphes/ht Normal file
View File

View File

@@ -0,0 +1,368 @@
#!/usr/bin/python3
# -*- Coding: utf-8 -*-
class Graphe_Oriente(object):
"""
Classe des Graphes Orientés (GO).
Les graphes sont représentés par
- une liste de sommets
- leur liste d'adjacence
Attributs (publics)
- sommets : liste des sommets
- voisins : dictionnaire des listes d'adjacence
Méthodes (publiques)
- ajoute_sommet : ajout d'un sommet
- ajoute_arcs : ajout d'un arc
"""
def __init__(self):
self.sommets = list()
self.voisins = dict()
return None
def ajoute_sommet(self, s):
"""
Ajoute le sommet s à l'instance de GO.
:param s data: etiquette du sommet
"""
self.sommets.append(s)
self.voisins[s] = list()
return None
def ajoute_arc(self, origine, extremite):
"""
Ajoute l'arc origine -> extremite à l'instance de GO.
:param self Graphe_Oriente: instance à laquelle ajouter un arc
:param origine data: un sommet de l'instance, origine de l'arc
:param extremite data: un sommet de l'instance, extremité de l'arc
:return None:
:effet de bord: Modification de l'instance
:pré-condition: les sommets doivent exister
"""
assert origine in self.sommets, "{} inconnu".format(origine)
assert extremite in self.sommets, "{} inconnu".format(extremite)
self.voisins[origine].append(extremite)
return None
def construire_chemins(graphe, depart):
"""
Construit tous les cheminement du graphe de depart donné
:param graphe GO: graphe à parcourir
:param depart data: sommet de départ dans le graphe
:return resultat dict: dictionnaire
- clef : sommet atteint
- valeur : tuple (longueur itinéraire, sommet prédécesseur)
:effet de bord: Aucun
>>> graphe = Graphe_Oriente()
>>> graphe.ajoute_sommet('A')
>>> graphe.ajoute_sommet('B')
>>> graphe.ajoute_sommet('C')
>>> graphe.ajoute_arc('A', 'B')
>>> graphe.ajoute_arc('B', 'C')
>>> construire_chemins(graphe, 'A')
{'A': (0, None), 'B': (1, 'A'), 'C': (2, 'B')}
"""
resultat = dict()
file = [depart] # initialise une file (sous forme de liste ici)
resultat[depart] = (0, None) # None car le depart n'a aucun predesseceur
while len(file) > 0:
sommet = file.pop(0) # retire le premier element de la file -> FIFO
for voisin in graphe.voisins[sommet]: # Parcours tous les voisins du sommet
if (
voisin not in resultat
): # Verifie que cette partie de l arbre (ou du graphe) n a pas deja ete explorer
distance = (
resultat[sommet][0] + 1
) # Definie distance -> Distance du dernier sommet + 1
resultat[voisin] = (
distance,
sommet,
) # Insere le nouveau sommet dans le dictionnaire
file.append(
voisin
) # Permet la reiteration de la boucle a partir de se sommet
return resultat
def reconstruire_chemin_vers(dico_chemins, *arrivee):
"""
Remonte tout l'itinéraire depuis un sommet fixé
vers un ou des sommets du graphe
:param dico_chemins dict: table des longueurs/prédécessurs
:param *arrivee: nombre quelconque de sommet à atteindre
(si vide, on considère tous les sommets)
:return resultat list: liste des chemins ie des listes de sommets traversés
>>> dico_chemins = {'A': (0, None), 'B': (1, 'A'), 'C': (2, 'B')}
>>> reconstruire_chemin_vers(dico_chemins, 'C')
[['A', 'B', 'C']]
"""
chemins = list()
cibles = arrivee # Creer une liste avec les sommets a remonter
if len(cibles) == 0:
return list(
dico_chemins.keys()
) # si la liste est vide, on renvoie les chemins (sans leurs attributs)
for sommet in cibles:
sous_chemin = []
current = sommet
while current is not None:
sous_chemin.insert(
0, current
) # on insere le sommet au début de la liste (permet de maintenir l ordre)
current = dico_chemins[current][
1
] # on change current avec le sommet predesseceur pour que la boucle continue
chemins.append(sous_chemin)
return chemins
def affichage_chemin(chemin):
"""
Produit le texte d'affichage d'un chemin
:param chemin list: liste des sommets constituant le chemin
:return string: chemin mis en forme pour affichage
"""
long = len(chemin) - 1
return "{} etapes :\n".format(long) + "\n\t-> ".join(
[str(sommet) for sommet in chemin]
)
# DEFINITION DES OPERATIONS DE TRANSITION
def vider(numero, etat):
"""
Vide un bidon
:param numero INT: index du bidon
:param etat tuple: etat des bidons avant l'opération
:return tuple: nouvel etat aprés opération
:effet de bord: aucun
>>> vider(1,(3,5,2))
(3, 0, 2)
"""
index_b = list(etat)
index_b[numero] = 0
return tuple(index_b)
def remplir(numero, etat, capacites):
"""
Remplit un bidon
:param numero INT: index du bidon
:param etat tuple: etat des bidons avant l'opération
:param capacites tuple: capacités des bidons
:return tuple: nouvel etat aprés opération
:effet de bord: aucun
>>> remplir(1, (3, 2, 1), (5, 4, 3))
(3, 4, 1)
"""
cap = list(capacites)
index_b = list(etat)
index_b[numero] = cap[numero]
return tuple(index_b)
def transvaser(source, destination, etat, capacites):
"""
Transvase un bidon dans un autre
:param origine int: index du bidon source
:param destination int: index du bidon destination
:param etas tuple: etat des bidons avant l'opération
:param capacites tuple: capacités des bidons
:return tuple: nouvel etat aprés opération
:effet de bord: aucun
>>> transvaser(0, 1, (3, 2, 1), (5, 4, 3))
(1, 4, 1)
"""
transfer_amount = min(
list(etat)[source], list(capacites)[destination] - list(etat)[destination]
)
new_state = list(etat)
new_state[source] -= transfer_amount
new_state[destination] += transfer_amount
return tuple(new_state)
# FONCTION LIEES AU GRAPHE DU WATER_JUG
# Pour construire les etats
def produit_cartesien(*listes):
"""
Concatène les tuples issus des différentes listes
:param *listes: un nombre quelconque de listes de tuples
:return list: une liste des tuples concaténés
Exemple
--------
>>> produit_cartesien([(1,2), (3, 4)], [(5, 6), (7, 8,)])
[(1, 2, 5, 6), (1, 2, 7, 8), (3, 4, 5, 6), (3, 4, 7, 8)]
"""
if listes == None:
return []
if len(listes) == 1:
return listes[0]
result = []
for elt in listes[0]:
for tuples in produit_cartesien(*listes[1:]):
result.append(elt + tuples)
return result
def creer_water_jug(*capacites):
"""
Création du graphe de Water Jug en fonction des capacités des bidons
:param *capacites: capacités des bidons disponibles
:return resultat GO: le graphe orienté du W_J
:pre-condition: au moins 2 bidons
:effet de bord: Aucun
"""
nb_bidons = len(capacites)
assert nb_bidons >= 2, "Pas assez de bidons"
resultat = Graphe_Oriente()
# CREATION DES SOMMETS
etats_marginaux = [
[(contenu,) for contenu in range(1 + capacite)] for capacite in capacites
]
etats_systeme = produit_cartesien(*etats_marginaux)
for etat in etats_systeme:
resultat.ajoute_sommet(etat)
# CREATION DES TRANSITIONS
for sommet in resultat.sommets:
voisins = list()
# VIDER
for bidon in range(nb_bidons):
voisin = vider(bidon, sommet)
if voisin != sommet and not (voisin in voisins):
voisins.append(voisin)
# REMPLIR
for bidon in range(nb_bidons):
voisin = remplir(bidon, sommet, capacites)
if voisin != sommet and not (voisin in voisins):
voisins.append(voisin)
# TRANSVASER
for origine in range(nb_bidons):
for destination in range(nb_bidons):
if origine != destination:
voisin = transvaser(origine, destination, sommet, capacites)
if voisin != sommet and not (voisin in voisins):
voisins.append(voisin)
# CREATION LISTES ADJACENCE
for voisin in voisins:
resultat.ajoute_arc(sommet, voisin)
return resultat
def atteindre(quantite, graphe_water_jug, depart=None, plus_court=False):
"""
Résolution du problème pour une quantite donnée
:param quantite int: la quantité à mesurer
:param graphe_water_jug GO: le graphe de la situation
:param depart tuple: etat initial, sommet d'origine dans le graphe
(bidons tous vides si depart est None, ie non fourni)
:param plus_court bool: si vrai, seul le(s) plus court(s) chemin(s)
est (sont) retenu(s)
:return list: la liste des tuples (sommet arrivee, nb d'etapes, ititneraire) ok
"""
if depart is None:
nb_bidons = len(graphe_water_jug.sommets[0])
depart = tuple([0 for _ in range(nb_bidons)])
chemins = construire_chemins(graphe_water_jug, depart)
resultat = list()
for sommet in chemins:
if quantite in sommet:
longueur, _ = chemins[sommet]
chemin = reconstruire_chemin_vers(chemins, sommet).pop()
index = len(resultat)
resultat.append((sommet, longueur, chemin))
while index > 0:
if resultat[index - 1][1] < longueur:
break
else:
resultat[index], resultat[index - 1] = (
resultat[index - 1],
resultat[index],
)
index -= 1
if len(resultat) < 2 or not (plus_court):
return resultat
mini = resultat[0][1]
return [element for element in resultat if element[1] == mini]
def main():
solutions = atteindre(4, creer_water_jug(3, 5))
for sommet_final, nb_etapes, chemin in solutions:
print(f"sommet atteint: {sommet_final}, en {nb_etapes} etapes")
print(affichage_chemin(chemin))
print("--------------------------------------")
def question1():
print("Non pas tous les quantités de litres peuvent être représenter comme 6l.")
def question2():
for i in range(10):
resolutions = atteindre(i, creer_water_jug(3, 5), None, True)
for sommet_final, nb_etapes, chemin in resolutions:
print(f"sommet atteint: {sommet_final}, en {nb_etapes} etapes")
print(affichage_chemin(chemin))
print("--------------------------------------")
def question3():
solution = atteindre(4, creer_water_jug(3, 5), None, True)
for sommet_final, nb_etapes, chemin in solution:
print(f"sommet atteint: {sommet_final}, en {nb_etapes} etapes")
print(affichage_chemin(chemin))
print("--------------------------------------")
def question4():
solution = atteindre(1, creer_water_jug(3, 5, 9), None, True)
for sommet_final, nb_etapes, chemin in solution:
print(f"sommet atteint: {sommet_final}, en {nb_etapes} etapes")
print(affichage_chemin(chemin))
print("--------------------------------------")
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)
main()
question1()
question2()
question3()
question4()

View File

@@ -0,0 +1,20 @@
def produit_cartesien(*listes):
if not listes:
return []
if len(listes) == 1:
return listes[0]
if len(listes) == 2:
liste1, liste2 = listes
resultat = list()
for tuple_1 in liste1:
for tuple_2 in liste2:
resultat.append(tuple_1 + tuple_2)
return resultat
liste1, *reste = listes
return produit_cartesien(liste1, produit_cartesien(*reste))
liste1 = [(_,) for _ in range(3)]
liste2 = [(_,) for _ in range(5)]
liste3 = [(_,) for _ in range(8)]
print(produit_cartesien(liste1, liste2))

View File

@@ -0,0 +1,35 @@
class leaudibidon(object):
def __init__(self, capacity):
self.capacity = capacity
self.quantity = 0
def fullfill_b5(b5, b3):
b5.quantity = b5.capacity
print("Fullfill b5")
def fullfill_b3(b5, b3):
b3.quantity = b3.capacity
print("Fullfill b3")
def void_b5(b5, b3):
b5.quantity = 0
print("void b5")
def void_b3(b5, b3):
b3.quantity = 0
print("void b3")
def transfer_b5_b3(b5, b3):
transfer_amount = min(b5.quantity, b3.capacity - b3.quantity)
b5.quantity, b3.quantity = (
b5.quantity - transfer_amount,
b3.quantity + transfer_amount,
)
def transfer_b3_b5(b5, b3):
transfer_amount = min(b3.quantity, b5.capacity - b5.quantity)
b5.quantity, b3.quantity = (
b5.quantity + transfer_amount,
b3.quantity - transfer_amount,
)

View File

@@ -0,0 +1,20 @@
class Pile:
def __init__(self) -> None:
self.element = []
def empiler(self, element) -> None:
self.element.append(element)
def est_vide(self) -> bool:
return len(self.element) == 0
def defiler(self):
assert not self.est_vide(), "La pile est vide"
return self.element.pop()
def size(self) -> int:
return len(self.element)
def index(self, k):
assert not self.est_vide(), "La pile est vide"
return self.element[k]

View File

@@ -0,0 +1,20 @@
class Queue:
def __init__(self) -> None:
self.element = []
def enfiler(self, element):
self.element.append(element)
def est_vide(self):
return len(self.element) == 0
def defiler(self):
assert self.est_vide(), "La file est vide"
return self.element.pop(0)
def size(self):
return len(self.element)
def index(self, k):
assert self.est_vide(), "La file est vide"
return self.element[k]

View File

@@ -0,0 +1,7 @@
import maze_creator as mc
lab = mc.Labyrinth(10, 10)
lab.set_start_end((0, 0), (100, 100))
lab.generate_maze()
print(lab.__str__())

View File

@@ -0,0 +1,78 @@
import random as rnd
class Labyrinth:
def __init__(self, rows, cols) -> None:
self.rows = rows
self.cols = cols
# Grille initiale : 1 = mur, 0 = chemin
self.grid = [[1 for _ in range(cols)] for _ in range(rows)]
self.start = None
self.end = None
def voisins(self, x, y):
"""
Retourne les voisins valides (encore des murs) de la cellule (x, y).
"""
directions = [(0, 2), (0, -2), (2, 0), (-2, 0)]
voisins = []
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < self.rows and 0 <= ny < self.cols and self.grid[nx][ny] == 1:
voisins.append((nx, ny))
return voisins
def casser_mur(self, x1, y1, x2, y2):
"""
Casse le mur entre les cellules (x1, y1) et (x2, y2).
"""
mx, my = (x1 + x2) // 2, (y1 + y2) // 2 # Coordonnées du mur à casser
self.grid[mx][my] = 0 # Transformer le mur en chemin
self.grid[x2][y2] = 0 # Transformer la cellule voisine en chemin
def generate_maze(self):
"""
Génère un labyrinthe parfait à l'aide de l'algorithme de Prim.
"""
# Choisir une cellule de départ aléatoire
x, y = rnd.randrange(0, self.rows, 2), rnd.randrange(0, self.cols, 2)
self.grid[x][y] = 0 # Transformer la cellule en chemin
# Liste des murs candidats (voisins de la cellule initiale)
murs = self.voisins(x, y)
while murs:
# Choisir un mur aléatoire
nx, ny = rnd.choice(murs)
murs.remove((nx, ny))
# Trouver une cellule voisine qui est déjà un chemin
for dx, dy in [(-2, 0), (2, 0), (0, -2), (0, 2)]:
cx, cy = nx + dx, ny + dy
if (
0 <= cx < self.rows
and 0 <= cy < self.cols
and self.grid[cx][cy] == 0
):
# Casser le mur entre les deux cellules
self.casser_mur(cx, cy, nx, ny)
# Ajouter les nouveaux murs adjacents
murs.extend(self.voisins(nx, ny))
break
def set_start_end(self, start, end):
"""
Définit les points de départ et d'arrivée du labyrinthe.
"""
self.start = start
self.end = end
def __str__(self) -> str:
"""
Représente le labyrinthe sous forme de chaîne de caractères.
"""
return "\n".join(
"".join(" " if cell == 0 else "#" for cell in row) for row in self.grid
)

View File

@@ -0,0 +1,54 @@
from fifo import (
Pile,
) # Remplacez "fifo" par le nom exact de votre fichier contenant la classe Pile
# Initialisation
pile = Pile()
# Test de empiler
pile.empiler(5)
pile.empiler(10)
assert pile.size() == 2, "Erreur : La taille de la pile devrait être 2"
assert pile.element == [5, 10], "Erreur : Les éléments de la pile ne correspondent pas"
# Test de est_vide
assert not pile.est_vide(), "Erreur : La pile ne devrait pas être vide"
pile.defiler()
pile.defiler()
assert (
pile.est_vide()
), "Erreur : La pile devrait être vide après avoir défiler tous les éléments"
# Test de defiler
pile.empiler(7)
pile.empiler(3)
assert pile.defiler() == 3, "Erreur : Le dernier élément défilé devrait être 3"
assert pile.defiler() == 7, "Erreur : Le dernier élément défilé devrait être 7"
try:
pile.defiler()
assert False, "Erreur : defiler devrait lever une exception pour une pile vide"
except AssertionError as e:
pass # Test réussi
# Test de size
pile.empiler(4)
assert pile.size() == 1, "Erreur : La taille de la pile devrait être 1"
pile.defiler()
assert pile.size() == 0, "Erreur : La taille de la pile devrait être 0 après defiler"
# Test de index
pile.empiler(1)
pile.empiler(2)
pile.empiler(3)
assert pile.index(0) == 1, "Erreur : L'élément à l'index 0 devrait être 1"
assert pile.index(2) == 3, "Erreur : L'élément à l'index 2 devrait être 3"
try:
pile.defiler()
pile.defiler()
pile.defiler()
pile.index(0)
assert False, "Erreur : index devrait lever une exception pour une pile vide"
except AssertionError as e:
pass # Test réussi
print("Tous les tests sont passés avec succès !")

View File

@@ -0,0 +1,111 @@
def construire_bfs(graphe: dict, origine: str) -> dict:
"""
Construit un arbre BFS à partir de graphe et d'un sommet d'origine.
Args:
graphe (dict): Un dictionnaire représentant le graphe, où les clés sont les sommets et les valeurs sont des listes de sommets adjacents.
origine (str): Le sommet de départ pour la recherche en largeur.
Returns:
dict: Un dictionnaire représentant l'arbre BFS, où les clés sont les sommets et les valeurs sont des tuples contenant la distance depuis l'origine et le sommet precédent.
Examples:
>>> graphe = {
... '1': ['2', '3'],
... '2': ['4', '5'],
... '3': ['6'],
... '4': [],
... '5': ['6'],
... '6': []
... }
>>> construire_bfs(graphe, '1')
{'1': (0, None), '2': (1, '1'), '3': (1, '1'), '4': (2, '2'), '5': (2, '2'), '6': (2, '3')}
>>> graphe = {
... 'A': ['B', 'C'],
... 'B': ['D'],
... 'C': ['E'],
... 'D': [],
... 'E': []
... }
>>> construire_bfs(graphe, 'A')
{'A': (0, None), 'B': (1, 'A'), 'C': (1, 'A'), 'D': (2, 'B'), 'E': (2, 'C')}
"""
couleur = dict()
resultat = dict()
for som in graphe.keys():
couleur[som] = 0
frontiere = [origine]
resultat[origine] = (0, None)
couleur[origine] = 1
while len(frontiere) > 0:
courant = frontiere.pop(0)
longueur, precedent = resultat[courant]
for som in graphe[courant]:
if couleur[som] == 0:
frontiere.append(som)
resultat[som] = (longueur + 1, courant)
couleur[som] = 1
couleur[courant] = 2
return resultat
def construire_dfs(graphe: dict, origine: str) -> dict:
"""
Construit un arbre DFS à partir de graphe et d'un sommet d'origine.
Args:
graphe (dict): Un dictionnaire représentant le graphe, où les clés sont les sommets et les valeurs sont des listes de sommets adjacents.
origine (str): Le sommet de départ pour la recherche en profondeur.
Returns:
dict: Un dictionnaire représentant l'arbre DFS.
Examples:
>>> graphe = {
... '1': ['2', '3'],
... '2': ['4', '5'],
... '3': ['6'],
... '4': [],
... '5': ['6'],
... '6': []
... }
>>> construire_dfs(graphe, '1')
{'1': (0, None), '2': (1, '1'), '3': (1, '1'), '6': (2, '3'), '4': (2, '2'), '5': (2, '2')}
>>> graphe = {
... 'A': ['B', 'C'],
... 'B': ['D'],
... 'C': ['E'],
... 'D': [],
... 'E': []
... }
>>> construire_dfs(graphe, 'A')
{'A': (0, None), 'B': (1, 'A'), 'C': (1, 'A'), 'E': (2, 'C'), 'D': (2, 'B')}
"""
couleur = dict()
resultat = dict()
for som in graphe.keys():
couleur[som] = 0
frontiere = [origine]
resultat[origine] = (0, None)
couleur[origine] = 1
while len(frontiere) > 0:
courant = frontiere.pop()
longueur, precedent = resultat[courant]
for som in graphe[courant]:
if couleur[som] == 0:
frontiere.append(som)
resultat[som] = (longueur + 1, courant)
couleur[som] = 1
couleur[courant] = 2
return resultat
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)
print(construire_dfs(g, "1"))

0
high-school/graphes/why Normal file
View File