diff --git a/Algorithmique/TD/TD0.md b/Algorithmique/TD/TD0.md index 13779df5675426f1b987fe955b391bc090dcf8fd..9355fdec5b7ee2fe6e0973d6da9b088eaeaa7464 100644 --- a/Algorithmique/TD/TD0.md +++ b/Algorithmique/TD/TD0.md @@ -1,3 +1,11 @@ +--- +title: TD0 +author: VAN DE MERGHEL Robin +date: 2023 +lang: fr +geometry: margin=2cm +--- + # Exercice 0.2 ## 1. diff --git a/Algorithmique/TD/TD0.pdf b/Algorithmique/TD/TD0.pdf index 05325e48fc09b4209ff96da3fc0557dd61f67c19..6808765b1975a533449248de115ba03a1f9d66cc 100644 Binary files a/Algorithmique/TD/TD0.pdf and b/Algorithmique/TD/TD0.pdf differ diff --git a/Algorithmique/TD/TD1.md b/Algorithmique/TD/TD1.md index aeccbe2313a75d827a957c2d1b52f2bcf5898cf6..f6d55848b2dc5b6417e7580c2b3399d4c61c426a 100644 --- a/Algorithmique/TD/TD1.md +++ b/Algorithmique/TD/TD1.md @@ -1,3 +1,11 @@ +--- +title: TD1 +author: VAN DE MERGHEL Robin +date: 2023 +lang: fr +geometry: margin=2cm +--- + # 1.3 ## 1. diff --git a/Algorithmique/TD/TD1.pdf b/Algorithmique/TD/TD1.pdf index 819864f2e6ace2c235a3510c9f816b9b07588e43..3051383c078594604e54f24a1b888de52b80c10e 100644 Binary files a/Algorithmique/TD/TD1.pdf and b/Algorithmique/TD/TD1.pdf differ diff --git a/Algorithmique/TD/TD2.md b/Algorithmique/TD/TD2.md index 68d30cb553fb2ff6fb7778e0b1dd3dcf34a5aa7e..414a1bc0a1f5eae82be090076355278409ac9117 100644 --- a/Algorithmique/TD/TD2.md +++ b/Algorithmique/TD/TD2.md @@ -389,4 +389,171 @@ Liste concatener(Liste l1, Liste l2) { } ``` -Le temps est bien en $O(1)$ car on ne fait que modifier les pointeurs. \ No newline at end of file +Le temps est bien en $O(1)$ car on ne fait que modifier les pointeurs. + + + +## Question 2 + +> **Expliquez comment utiliser les listes pour implémenter le TDA ensemble disjoint. Vous pourrez utiliser la réponse à la question précédente.** + +**Rappel du cours :** + +- `creerPartition(int n)` crée une partition où chaque partie est un singleton de $[n]$ +- `trouver(partie p, int x)` doit donner le représentant de la partie contenant $x$ +- `union(partie p, int x, int y)` doit faire l'union de sparties contenant $x$ et $y$ + +### Ma version + +```ruby +enregistrement Liste { + Element[] tab + int taille +} + +enregistrement Element { + int val + Partie p +} + +enregistrement Partie { + int id + Element debutPartie + Element finPartie +} +``` + +```ruby +Partie vide = -1 +Element vide = -1 + +# O(1) +# Car on a juste une allocation +Element creerElement(int x) { # x est un entier + Element e = allocation() + e.val = x + e.p = vide + return e +} + +# O(n) +# Car on doit allouer n éléments +# Chaque allocation est en O(1) +Liste creerPartition(int n) { + Liste l = allocation() + l.tab = allocation(n) + l.taille = n + for (int i = 0; i < n; i++) { + l.tab[i] = creerElement(i) + } + return l +} + +# O(1) +# Car on a un tableau, et on peut accéder à l'élément en O(1) +Partie trouver(Partie p, int x) { + return p.tab[x].p +} + +# O(n) +# Car pour chaque élément de la liste, on doit changer le pointeur +# Changer le pointeur est en O(1) +void union(Partie p, int x, int y) { + + Partie px = trouver(p, x) + Partie py = trouver(p, y) + + Partie plusGrand + Partie plusPetit + + Si taille(px) > taille(py) { + plusGrand = px + plusPetit = py + } Sinon { + plusGrand = py + plusPetit = px + } + + Element e = plusPetit.debutPartie + Tant que e != vide, Faire { + e.p = plusGrand + e = e.suiv + plusPetit.finPartie.suiv = e + plusPetit.finPartie = e + } + + plusGrand.finPartie.suiv = plusPetit.debutPartie + plusGrand.finPartie = plusPetit.finPartie + + return p + +} +``` + + +### Correction + +```ruby +enregistrement Partition { + Liste<Liste<int>> parties +} + +Partition creerPartition(int n) { + Partition p; + p.l = creerLise(); + + Pour i <- 1 à n, Faire { + s = creerListe(); + insererDebut(s, i); + insererDebut(p.l, s); + } + + Retourner p; +} + +int trouver(Partition p, int x) { + Cellule c = debutliste(p.l) + Tant que finListe(c) == Faux, Faire { + Cellule c2 = debutListe(val(c)) + + Tant que finListe(c2) == Faux { + Si x == valeur(c2) { + Retourner valeur(c) + } + c2 = suivant(c2) + } + + c = suivant(c) + } + + Retourner -1 +} +``` + +Pour `trouver` : On suppose toujours que valeur suivant et debutListe sont en $O(1)$. + +La boucle `Tant Que` est extérieure fait au plus $k$ itérations, $k$ le nombre de parties. + +Pour chaque itération $i$, le nombre maximum d'itérations de la boucle est le nombre d'éléments de la partie parcourure à l'itération $i$, nommons cette taille $n_i$. + +Complexité en temps : + +$$\sum_{i=1}^k n_i$$ + +Comme les parties sont disjointes, et que l'union des parties c'est l'ensemble à partitionner, $\sum_{i=1}^k = n_i = n$, où $n$ est la taille de l'ensemble à partitionner. + +Donc, la complexité est $\sum_{i=1}^k n_i = n$. + +```ruby +Partition union(Partition p, int x, int y) { + l1 = trouver2(p,x) # O(n) + l2 = trouver2(p,y) # O(n) + concaténer(l1,l2) # O(n) ou O(1) selon l'implémentation + supprimer(p.l,l2) # O(nombre de parties) <= O(n) + retourner p +} + +# On définit trouver 2 : +# Même chose que trouver, sauf que l'on retourne la liste contenant x +# n = la taille de l'ensemble partitionné par p +``` diff --git a/Algorithmique/TD/TD2.pdf b/Algorithmique/TD/TD2.pdf index b3b24b1c63282c1d99a274170e5fb35db547d469..fecee3f16b6201bf1d930972758e7bc5d624fae6 100644 Binary files a/Algorithmique/TD/TD2.pdf and b/Algorithmique/TD/TD2.pdf differ diff --git a/Algorithmique/TD/TD3.md b/Algorithmique/TD/TD3.md new file mode 100644 index 0000000000000000000000000000000000000000..ee313dc959285cbc72f853dd93bfcf85f514ecbd --- /dev/null +++ b/Algorithmique/TD/TD3.md @@ -0,0 +1,188 @@ +# Exercice 3.1 + +## Question 1 + +> **On implémente une arborescence avec le tableau `PERE`, i.e. dans chaque case on stocke l'index du père, et pour la racine on stocke la valeur $-1$. Proposer un algorithme (avec la meilleure complexité) pour chacune des opérations suivantes :** +> - La racine de l'arbre +> - La hauteur $h$ de l'arbre +> - La liste des fils d'un noeud $x$ +> - Le sous-arbre issu d'un noeud $x$ +> - Tester si $x$ et $y$ sont frères + +```ruby +# Racine de l'arbre +# On cherche le premier élément du tableau qui vaut -1 +int racine(int[] pere) { + int i = 0; + while (pere[i] != -1) { + i++; + } + return i; +} +``` + +La complexité en temps $O(l)$, où $l$ est le nombre d'itérations de `Tant que`. Comme $l$ peut être égal à $n-1$, la complexité en temps est $O(n)$. + + +```ruby +# Hauteur de l'arbre +int hauteurNoeud(int Pere[n], int x) { + int h = 0; + int y = x; + + while (Pere[y] != -1) { + h = h + 1; + y = Pere[y]; + } + + return h + 1; +} +``` + +Si $h_x$ c'est la distance de la racine au noeud $x$, le nombre d'itérations c'est $h_x$. Donc la complexité en temps est $O(h_x)$. + +```ruby +# Hauteur de l'arbre +int hauteur(int Pere[n]) { + int h = 0; + int i = 0; + + for (i = 0; i < n; i++) { + int h_i = hauteurNoeud(Pere, i); + if (h_i > h) { + h = h_i; + } + } + + return h; +} +``` + +Complexité : $n$ itérations, chaque itérations $O(h_i)$. Donc au total $\sum_{i=1}^n O(h_i) = O(\sum_{i=1}^n h_i) = O(n \cdot h)$. + + +```ruby +# Liste des fils d'un noeud x +Liste listeFils(int Pere[n], int x) { + Liste liste = ListeVide(); + int i = 0; + + for (i = 0; i < n; i++) { + if (Pere[i] == x) { + liste = insererDebut(liste, i); + } + } + + return liste; +} +``` + +Si `insererDebut` est $O(1)$, la complexité en temps est $O(n)$. + +```ruby +bool frere(int Pere[n], int x, int y) { + retourner Pere[x] == Pere[y]; +} +``` + +La complexité en temps est $O(1)$ car on n'accède qu'à deux cases du tableau. + +```ruby +# Ensemble des feuilles +Liste feuilles(int Pere[n]) { + bool[n] f; + + # On met à vrai toutes les cases du tableau + for (i = 0; i < n; i++) { + f[i] = true; + } + + for (i=0; i < n; i++) { + f[Pere[i]] = false; + } + + Liste l = creerListe(); + for (i = 0; i < n; i++) { + if (f[i]) { + l = insererDebut(l, i); + } + } + + return l; + +} + +# Sous-arbre issu d'un noeud x +liste sousArbre(int Pere[n], int x) { + Liste l = feuilles(Pere); + + for (Feuille feuille in l) { + if (*En remontant comme dans la fonction hauteurNoeud on rencontre x*) { + *ajouter tous les noeuds entre f et x dans r* + } + } +} +``` + +On peut vérifier que la complexité en temps est $O(n)$. + +## Question 2 + +> **Comparer avec une implémentation où chaque noeud stocke une référence vers sont père, un premier fils vers sont frère de droite.** + +```mermaid +graph TD +A(Noeud) --> C(Pere) +A --> B(Frere de droite) +A --> D(Premier fils) +``` + +```mermaid +graph TD +5 --> 1 +1 --> 5 +1 --> 1 +5 --> 2 +2 --> 4 +4 --> 2 +2 --> 5 +2 --> 3 +3 --> 5 +3 --> 7 +7 --> 5 +``` + +Le graphe ci-dessus représente les relations de l'arbre suivant : + +```mermaid +graph TD +5 --> 1 +5 --> 2 +5 --> 3 +5 --> 7 +2 --> 4 +``` + +```ruby +enregistrement arbre { + noeud racine +} + +enregistrement noeud { + noeud pere + noeud filsG + noeud frereD + T val +} + +fils(noeud n) { + l = creerListe(); + noeud d = n.filsG; + + while (d != null) { + l = insererDebut(l, d); + d = d.frereD; + } + + return l; +} \ No newline at end of file diff --git a/Algorithmique/TD/TD4.md b/Algorithmique/TD/TD4.md index d4dc80af168af5b3daa9083ee54eacdb9828f37f..e715f6d575e5ae7eae8e26ca0dd1a9788f1db61e 100644 --- a/Algorithmique/TD/TD4.md +++ b/Algorithmique/TD/TD4.md @@ -1,3 +1,11 @@ +--- +title: TD4 Listes +author: VAN DE MERGHEL Robin +date: 2023 +lang: fr +geometry: margin=2cm +--- + # Exercice 4.1 > **On a les codes suivants :** @@ -84,20 +92,20 @@ On a donc $c_k \leq 1$ quand $n \geq \log_5(n)$. Donc la complexité est au pire On a : -$$\begin{align} +$$\begin{aligned} (n+a)^b &= \sum_{k=0}^b \binom{b}{k} a^{b-k} n^k \\ &= n^k + \alpha_1 n^{k-1} + \alpha_2 n^{k-2} + \dots + \alpha_{k-1} n + \alpha_k \\ -\end{align}$$ +\end{aligned}$$ Avec $\alpha_i = \binom{b}{i}$. On a donc : -$$\begin{align} +$$\begin{aligned} O((n+a)^b) &= O(n^b + \alpha_1 n^{b-1} + \alpha_2 n^{b-2} + \dots + \alpha_{b-1} n + \alpha_b) \\ &= O(n^b) + O(\alpha_1 n^{b-1}) + O(\alpha_2 n^{b-2}) + \dots + O(\alpha_{b-1} n) + O(\alpha_b) \\ &= O(n^b) + O(n^{b-1}) + O(n^{b-2}) + \dots + O(n) + O(1) \\ -\end{align}$$ +\end{aligned}$$ On a $O(1)$ négligeable par rapport à $O(n)$, puis $O(n)$ négligeable par rapport à $O(n^2)$, etc. Jusqu'à $O(n^{b-1})$ qui est négligeable par rapport à $O(n^b)$. Donc $O((n+a)^b) = O(n^b)$. diff --git a/Algorithmique/TD/TD4.pdf b/Algorithmique/TD/TD4.pdf new file mode 100644 index 0000000000000000000000000000000000000000..601f4643c9ed306f4ec4142130f92495370c1b66 Binary files /dev/null and b/Algorithmique/TD/TD4.pdf differ diff --git a/Algorithmique/TD/td-listes.pdf b/Algorithmique/TD/td-listes.pdf new file mode 100644 index 0000000000000000000000000000000000000000..37021f44e45277da09c04968722e981b952c8248 Binary files /dev/null and b/Algorithmique/TD/td-listes.pdf differ