diff --git a/slides/cours_15.md b/slides/cours_15.md new file mode 100644 index 0000000000000000000000000000000000000000..bfd39d7052deb8287ecb42bc4f12c75f20b9e465 --- /dev/null +++ b/slides/cours_15.md @@ -0,0 +1,1140 @@ +--- +title: "Fin des tables de hachages et arbres" +date: "2025-02-28" +--- + +# Rappel + +* Qu'est-ce qu'une table de hachage? + +. . . + +* Structure de données abstraite où chaque *valeur* (ou élément) est associée à une *clé* (ou argument). +* Quelles sont les fonctions typiques définies sur les tables? + +. . . + +* Insetion, consultation, suppression. + + ```C + void insert(table, key, value) + value get(table, key) + value remove(table, key) + ``` + +* Comment fait-on le lien entre une clé et une valeur dans le tableau? + +. . . + +* On hache! + +# Exercice 2 + +* Reprendre l'exercice 1 et utiliser la technique de double hachage pour traiter + les collisions avec + +\begin{align*} +h_1(k)&=k\mod 13,\\ +h_2(k)&=1+(k\mod 11). +\end{align*} +* La fonction de hachage est donc $h(k)=(h(k)+h_2(k)) \% 13$ en cas de + collision. + + +# Exercice 3 + +* Stocker les numéros de téléphones internes d'une entreprise suivants dans un +tableau de 10 positions. +* Les numéros sont compris entre 100 et 299. +* Soit $N$ le numéro de téléphone, la fonction de hachage est +$$ +h(N)=N\mod 10. +$$ +* La fonction de gestion des collisions est +$$ +C_1(N,i)=(h(N)+3\cdot i)\mod 10. +$$ +* Placer 145, 167, 110, 175, 210, 215 (mettre son état à occupé). +* Supprimer 175 (rechercher 175, et mettre son état à supprimé). +* Rechercher 35. +* Les cases ni supprimées, ni occupées sont vides. +* Expliquer se qui se passe si on utilise? +$$ +C_1(N,i)=(h(N)+5\cdot i)\mod 10. +$$ + +# Préambule + +\small + +* On considère pas le cas du chaînage en cas de collisions. +* L'insertion est construite avec une forme du type + + ```C + index = h(key); + while (table[index].state == OCCUPIED + && table[index].key != key) { + index = (index + k) % table_size; // attention à pas dépasser + } + table[index].key = key; + table[index].state = OCCUPIED; + ``` +\normalsize + +* Gestion de l'état d'une case *explicite* + + ```C + typedef enum {EMPTY, OCCUPIED, DELETED} state; + ``` + +# L'insertion + +## Pseudocode? + +. . . + +```C +rien insertion(table, clé, valeur) { + index = hash(clé) + index = + tant que état(table[index]) == occupé et clé(table[index]) != clé: + index = rehash(clé) + + état(table[index]) = occupé + table[index] = valeur +} +``` + +# La suppression + +## Pseudocode? + +. . . + +```C +valeur suppression(table, clé): + index = hash(clé) + tant que état(table[index]) != vide: + si état(table[index]) == occupé et clé(table[index]) == clé: + état(table[index]) = supprimé + sinon + index = rehash(clé) +} +``` + +# La recherche + +## Pseudocode? + +. . . + +```C +booléen recherche(table, clé) { + index = hash(clé) + tant que état(table[index]) != vide: + si état(table[index]) == occupé et clé(table[index]) == clé: + retourner vrai + sinon + index = rehash + retourner faux +} +``` + +# Écrivons le code! + +* Mais avant: + * Quelles sont les structures de données dont nous avons besoin? + * Y a-t-il des fonctions auxiliaires à écrire? + * Écrire les signatures des fonctions. + +. . . + +## Structures de données + +\footnotesize + +. . . + +```C +typedef enum {empty, deleted, occupied}; +typedef ... key_t; +typedef ... value_t; +typedef struct _cell_t { + key_t key; + value_t value; + state_t state; +} cell_t; +typedef struct _hm { + cell_t *table; + int capacity; + int size; +} hm; +``` + +# Écrivons le code! + +## Fonctions auxiliaires + +. . . + +```C +static int hash(key_t key); +static int rehash(int index, key_t key); +static int find_index(hm h, key_t key); +``` + +## Signature de l'API + +. . . + +```C +void hm_init(hm *h, int capacity); +void hm_destroy(hm *h); +bool hm_set(hm *h, key_t key, value_t *value); +bool hm_get(hm h, key_t key, value_t *value); +bool hm_remove(hm *h, key_t key, value_t *value); +bool hm_search(hm h, key_t key); +void hm_print(hm h); +``` + +# Live code session! + +0. Offered to you by ProtonVPN[^1]! + +. . . + +1. Like the video. +2. Subscribe to the channel. +3. Use our one time voucher for ProtonVPN: `PAULISAWESOME`. +4. Consider donating on our patreon. + +[^1]: The fastest way to connect to BBB! + +# Les arbres + +\Huge Les arbres + +# Les arbres: définition + +"Un arbre est un graphe acyclique orienté possédant une unique racine, et tel que tous les nœuds sauf la racine ont un unique parent." + +. . . + +**Santé!** + +## Plus sérieusement + +* Ensemble de **nœuds** et d'**arêtes** (graphe), +* Les arêtes relient les nœuds entre eux, mais pas n'importe comment: chaque + nœud a au plus un **parent**, +* Le seul nœud sans parent est la **racine**, +* Chaque nœud a un nombre fini d'**enfants**, +* La hiérarchie des nœuds rend les arêtes **orientées** (parent -> enfants), et empêche les + **cycles** (acyclique, orienté). +* La **feuille** ou **nœud terminal** est un nœud sans enfants, +* Le **niveau** est 1 à la racine et **niveau+1** pour les enfants, +* Le **degré** d'un nœud est le nombre de enfants du nœud. + +. . . + +* Chaque nœud est un arbre en lui même. +* La **récursivité** sera très utile! + + +# Arbre ou pas arbre? + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + 1-->2; + 1-->3; + 3-->2; + 3-->4; + 3-->5; +``` +:::: + +. . . + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + 1-->2; + 1-->3; + 3-->4; + 3-->5; + 3-->6; +``` +:::: + +::: + +# Arbre ou pas arbre? + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + 1-->2; + 1-->3; + 3-->4; + 3-->5; + 3-->6; + 6-->7; + 7-->3; +``` +:::: + +. . . + +:::: column +```{.mermaid format=pdf width=300 loc=figs/} +graph TD; + 1; +``` +:::: + +::: + +# Arbre ou pas arbre? + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + 1---2; + 1---3; + 3---4; + 3---5; +``` +:::: + +. . . + +:::: column +```{.mermaid format=pdf width=300 loc=figs/} +graph BT; + 1-->2; + 1-->3; + 3-->4; + 3-->5; + 3-->6; +``` +:::: + +::: + +# Degré et niveau + +* Illustration du degré (nombre d'enfants) et du niveau (profondeur) + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + 1[degré 2]-->2[degré 0]; + 1-->3[degré 3]; + 3-->4[degré 0]; + 3-->5[degré 0]; + 3-->6[degré 0]; +``` +:::: + +. . . + +:::: column +```{.mermaid format=pdf width=300 loc=figs/} +graph TD; + 1[niveau 1]-->2[niveau 2]; + 1-->3[niveau 2]; + 3-->4[niveau 3]; + 3-->5[niveau 3]; + 3-->6[niveau 3]; +``` +:::: + +::: + +* Les nœuds de degré 0, sont des feuilles. + +# Application: recherche rapide + +## Pouvez vous construire un arbre pour résoudre le nombre secret? + + . . . + +* Le nombre secret ou la recherche dichotomique (nombre entre 0 et 10). + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph LR; + 5-->|<|2; + 5-->|>|7; + 7-->|>|8; + 7-->|<|6; + 8-->|>|9; + 9-->|>|10; + 2-->|<|1; + 2-->|>|3; + 3-->|>|4; + 1-->|<|0; +``` +:::: + +:::: column + +**Question:** Quelle est la complexité pour trouver un nombre? + +:::: + +::: + +# Autres représentation + +* Botanique +* **Exercice:** Ajouter les degrés/niveaux et feuilles + +```{.mermaid width=250 format=pdf loc=figs/} +graph TD; + A-->B; + A-->C; + B-->D; + B-->E; + B-->F; + F-->I; + F-->J; + C-->G; + C-->H; + H-->K; +``` + +# Autres représentation + +* Ensembliste + +::: columns + +:::: column +```{.mermaid width=300 format=pdf loc=figs/} +graph TD; + A-->B; + A-->C; + B-->D; + B-->E; + B-->F; + F-->I; + F-->J; + C-->G; + C-->H; + H-->K; +``` +:::: + +. . . + +:::: column + +:::: + +::: + +# Autres représentation + +* Liste + +::: columns + +:::: column +```{.mermaid width=400 format=pdf loc=figs/} +graph TD; + A-->B; + A-->C; + B-->D; + B-->E; + B-->F; + F-->I; + F-->J; + C-->G; + C-->H; + H-->K; +``` +:::: + +. . . + +:::: column +``` +(A + (B + (D) + (E) + (F + (I) + (J) + ) + ) + (C + (G) + (H + (K) + ) + ) +) +``` +:::: + +::: + +# Autres représentation + +* Par niveau + +::: columns + +:::: column +```{.mermaid width=400 format=pdf loc=figs/} +graph TD; + A-->B; + A-->C; + B-->D; + B-->E; + B-->F; + F-->I; + F-->J; + C-->G; + C-->H; + H-->K; +``` +:::: + +. . . + +:::: column +``` +1 2 3 4 +------------------------- +A + B + D + E + F + I + J + C + G + H + K +``` +:::: + +::: + +# L'arbre binaire + +* Structure de données abstraite, +* Chaque nœud a au plus deux enfants: gauche et droite, +* Chaque enfants est un arbre. + +## Comment représenteriez vous une telle structure? + +. . . + +```C +<R, G, D> + R: racine + G: sous-arbre gauche + D: sous-arbre droite +``` + +## Comment cela s'écrirait en C? + +. . . + +```C +typedef struct _node { + contenu info; + struct _node *left, *right; +} node; +typedef node *tree; +``` + +# L'arbre binaire + +## Que se passerait-il avec + +```C +typedef struct _node { + int info; + struct _node left, right; +} node; +``` + +* On ne sait pas quelle est la taille de node, on ne peut pas l'allouer! + +## Interface minimale + +* Qu'y mettriez vous? + +. . . + +```C +NULL -> arbre (vide) +<n, arbre, arbre> -> arbre +visiter(arbre) -> nœud (la racine de l'arbre) +gauche(arbre) -> arbre (sous-arbre de gauche) +droite(arbre) -> arbre (sous-arbre de droite) +``` + +* Les autres opérations (insertion, parcours, etc) dépendent de ce qu'on stocke + dans l'arbre. + +# Exemple d'arbre binaire + +* Représentez `(c - a * b) * (d + e / f)` à l'aide d'un arbre binaire (matrix) + +. . . + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + A[*]-->B[-]; + B-->C[c]; + B-->D[*]; + D-->E[a]; + D-->F[b]; + A-->G[+]; + G-->H[d]; + G-->I["/"]; + I-->J[e]; + I-->K[f]; +``` +:::: + + +:::: column + +## Remarques + +* L'arbre est **hétérogène**: le genre d'info est pas le même sur chaque nœud + (opérateur, opérande). + * Les feuilles contiennent les opérandes. + * Les nœuds internes contiennent les opérateurs. + +:::: + +::: + +# Parcours d'arbres binaires + +* Appliquer une opération à tous les nœuds de l'arbre, +* Nécessité de **parcourir** l'arbre, +* Utiliser uniquement l'interface: visiter, gauche, + droite. + +## Une idée de comment parcourir cet arbre? + +* 3 parcours (R: Racine, G: sous-arbre gauche, D: sous-arbre droit): + + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + A[*]-->B[-]; + B-->C[c]; + B-->D[*]; + D-->E[a]; + D-->F[b]; + A-->G[+]; + G-->H[d]; + G-->I["/"]; + I-->J[e]; + I-->K[f]; +``` +:::: + +:::: column + +1. Parcours **préfixe** (R, G, D), +2. Parcours **infixe** (G, R, D), +3. Parcours **postfixe** (G, D, R). + +:::: + +::: + +# Le parcours infixe (G, R, D) + +* Gauche, Racine, Droite: + 1. On descend dans l'arbre de gauche tant qu'il est pas vide, + 2. On visite la racine du sous arbre, + 3. On descend dans le sous-arbre de droite (s'il est pas vide), + 4. On recommence. + +. . . + +## Incompréhensible? + +* La récursivité c'est la vie. + +``` +parcours_infixe(arbre a) + si est_pas_vide(gauche(a)) + parcours_infixe(gauche(a)) + visiter(A) + si est_pas_vide(droite(A)) + parcours_infixe(droite(A)) +``` + +# Graphiquement (dessinons) + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + A[*]-->B[-]; + B-->C[c]; + B-->D[*]; + D-->E[a]; + D-->F[b]; + A-->G[+]; + G-->H[d]; + G-->I["/"]; + I-->J[e]; + I-->K[f]; +``` +:::: + +:::: column + +``` +parcours_infixe(arbre a) + si est_pas_vide(gauche(a)) + parcours_infixe(gauche(a)) + visiter(A) + si est_pas_vide(droite(A)) + parcours_infixe(droite(A)) +``` + +:::: + +::: + + +# Graphiquement (`mermaid` c'est super) + +::: columns + +:::: column +```{.mermaid format=pdf width=400 loc=figs/} +graph TD; + A[*]-->B[-]; + A[*]-.->|1|B[-]; + B-->C[c]; + B-.->|2|C[c]; + C-.->|3|B; + B-->D[*]; + B-.->|4|D; + D-->E[a]; + D-.->|5|E; + E-.->|6|D; + D-->F[b]; + D-.->|7|F; + F-.->|8|A; + A-->G[+]; + A-.->|9|G; + G-->H[d]; + G-.->|10|H; + H-.->|11|G; + G-->I["/"]; + G-.->|12|I; + I-->J[e]; + I-.->|13|J; + J-.->|14|I; + I-->K[f]; + I-.->|15|K; +``` +:::: + +:::: column + +``` +parcours_infixe(arbre a) + si est_pas_vide(gauche(a)) + parcours_infixe(gauche(a)) + visiter(A) + si est_pas_vide(droite(A)) + parcours_infixe(droite(A)) +``` + +## Remarque + +Le nœud est visité à la **remontée**. + +## Résultat + +``` +c - a * b * d + e / f +``` + +:::: + +::: + +# Et en C? + +## Live code + +\footnotesize + +. . . + +```C +typedef int data; +typedef struct _node { + data info; + struct _node* left; + struct _node* right; +} node; +typedef node* tree_t; +void tree_print(tree_t tree, int n) { + if (NULL != tree) { + tree_print(tree->left, n+1); + for (int i = 0; i < n; i++) { + printf(" "); + } + printf("%d\n", tree->info); + tree_print(tree->right, n+1); + } +} +``` + +# Question + +## Avez-vous compris le fonctionnement? + +. . . + +## Vous en êtes sûr·e·s? + +. . . + +## OK, alors deux exercices: + +1. Écrire le pseudo-code pour le parcours R, G, D (matrix). +2. Écrire le pseudo-code pour la parcours G, D, R (matrix), + +## Rappel + +``` +parcours_infixe(arbre a) + si est_pas_vide(gauche(a)) + parcours_infixe(gauche(a)) + visiter(A) + si est_pas_vide(droite(A)) + parcours_infixe(droite(A)) +``` + +# Correction + +\footnotesize + +* Les deux parcours sont des modifications **triviales**[^2] de l'algorithme + infixe. + +## Le parcours postfixe + +```python +parcours_postfixe(arbre a) + si est_pas_vide(gauche(a)) + parcours_postfixe(gauche(a)) + si est_pas_vide(droite(a)) + parcours_postfixe(droite(a)) + visiter(a) +``` + +## Le parcours préfixe + +```python +parcours_préfixe(arbre a) + visiter(a) + si est_pas_vide(gauche(a)) + parcours_préfixe(gauche(a)) + si est_pas_vide(droite(a)) + parcours_préfixe(droite(a)) +``` + +. . . + +**Attention:** L'implémentation de ces fonctions en C sont **à faire** en +exercice (inspirez vous de ce qu'on a fait avant)! + +# Exercice: parcours + +## Comment imprimer l'arbre ci-dessous? + +``` + f + / + e + + + d +* + c + - + b + * + a +``` + +. . . + +## Bravo vous avez trouvé! + +* Il s'agissait du parcours D, R, G. + +# Implémentation + +## Vous avez 5 min pour implémenter cette fonction et la poster sur matrix! + +. . . + +```C +void pretty_print(tree_t tree, int n) { + if (NULL != tree) { + pretty_print(tree->right, n+1); + for (int i = 0; i < n; ++i) { + printf(" "); + } + printf("%d\n", tree->info); + pretty_print(tree->left, n+1); + } +} +``` + +# Exercice supplémentaire (sans corrigé) + +Écrire le code de la fonction + +```C +int depth(tree_t t); +``` + +qui retourne la profondeur maximale d'un arbre. + +Indice: la profondeur à chaque niveau peut-être calculée à partir du niveau des +sous-arbres de gauche et de droite. + +# La recherche dans un arbre binaire + +* Les arbres binaires peuvent retrouver une information très rapidement. +* À quelle complexité? À quelle condition? + +. . . + +## Condition + +* Le contenu de l'arbre est **ordonné** (il y a une relation d'ordre (`<`, `>` + entre les éléments). + +## Complexité + +* La profondeur de l'arbre (ou le $\mathcal{O}(\log_2(N))$) + +. . . + +## Exemple: les arbres lexicographiques + +* Chaque nœud contient une information de type ordonné, la **clé**, +* Par construction, pour chaque nœud $N$: + * Toutes clé du sous-arbre à gauche de $N$ sont inférieurs à la clé de $N$. + * Toutes clé du sous-arbre à droite de $N$ sont inférieurs à la clé de $N$. + +# Algorithme de recherche + +* Retourner le nœud si la clé est trouvée dans l'arbre. + +```python +arbre recherche(clé, arbre) + tante_que est_non_vide(arbre) + si clé < clé(arbre) + arbre = gauche(arbre) + sinon si clé > clé(arbre) + arbre = droite(arbre) + sinon + retourne arbre + retourne NULL +``` + +# Algorithme de recherche, implémentation (live) + +\footnotesize + +. . . + +```C +typedef int key_t; +typedef struct _node { + key_t key; + struct _node* left; + struct _node* right; +} node; +typedef node* tree_t; +tree_t search(key_t key, tree_t tree) { + tree_t current = tree; + while (NULL != current) { + if (current->key > X) { + current = current->gauche; + } else if (current->key < X){ + current = current->droite; + } else { + return current; + } + } + return NULL; +} +``` + +# Exercice (5-10min) + +Écrire le code de la fonction + +```C +int tree_size(tree_t tree); +``` + +qui retourne le nombre total de nœuds d'un arbre et poster le résultat sur +matrix. + +Indication: la taille, est 1 + le nombre de nœuds du sous-arbre de gauche +additionné au nombre de nœuds dans le sous-arbre de droite. + +. . . + +```C +int arbre_size(tree_t tree) { + if (NULL == tree) { + return 0; + } else { + return 1 + tree_size(tree->left) + + tree_size(tree->right); + } +} +``` + +# L'insertion dans un arbre binaire + +* C'est bien joli de pouvoir faire des parcours, recherches, mais si on peut + pas construire l'arbre.... + +## Pour un arbre lexicographique + +* Rechercher la position dans l'arbre où insérer. +* Créer un nœud avec la clé et le rattacher à l'arbre. + +# Exemple d'insertions + +* Clés uniques pour simplifier. +* Insertion de 5, 15, 10, 25, 2, -5, 12, 14, 11. +* Rappel: + * Plus petit que la clé courante => gauche, + * Plus grand que la clé courante => droite. +* Faisons le dessins ensemble + +``` + + + + + + + + + +``` + +## Exercice (3min, puis matrix) + +* Dessiner l'arbre en insérant 20, 30, 60, 40, 10, 15, 25, -5 + + +# Pseudo-code d'insertion (1/2) + +* Deux parties: + * Recherche le parent où se passe l'insertion. + * Ajout de l'enfant dans l'arbre. + +## Recherche du parent + +``` +arbre position(arbre, clé) + si est_non_vide(arbre) + si clé < clé(arbre) + suivant = gauche(arbre) + sinon + suivant = droite(arbre) + tant que clé(arbre) != clé && est_non_vide(suivant) + arbre = suivant + si clé < clé(arbre) + suivant = gauche(arbre) + sinon + suivant = droite(arbre) + + retourne arbre +``` + +# Pseudo-code d'insertion (2/2) + +* Deux parties: + * Recherche de la position. + * Ajout dans l'arbre. + +## Ajout de l'enfant + +``` +ajout(arbre, clé) + si est_vide(arbre) + arbre = nœud(clé) + sinon + si clé < clé(arbre) + gauche(arbre) = nœud(clé) + sinon si clé > clé(arbre) + droite(arbre) = nœud(clé) + sinon + retourne +``` + +# Code d'insertion en C + +## Recherche du parent (ensemble) + +. . . + +```C +tree_t position(tree_t tree, key_t key) { + tree_t current = tree; + if (NULL != current) { + tree_t subtree = key > current->key ? current->right : + current->left; + while (key != current->key && NULL != subtree) { + current = subtree; + subtree = key > current->key ? current->right : + current->left; + } + } + return current; +} +``` + +[^2]: Copyright cours de mathématiques pendant trop d'années. diff --git a/slides/figs/ensemble.svg b/slides/figs/ensemble.svg new file mode 100644 index 0000000000000000000000000000000000000000..7901e86322f6a9b16a3ffdd40a00e71ce5be494c --- /dev/null +++ b/slides/figs/ensemble.svg @@ -0,0 +1,263 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="265.59039mm" + height="182.94089mm" + viewBox="0 0 265.59039 182.94089" + version="1.1" + id="svg5" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + sodipodi:docname="ensemble.svg" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview7" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:document-units="mm" + showgrid="false" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:zoom="0.28372389" + inkscape:cx="79.302451" + inkscape:cy="382.41404" + inkscape:window-width="1293" + inkscape:window-height="1022" + inkscape:window-x="14" + inkscape:window-y="44" + inkscape:window-maximized="1" + inkscape:current-layer="layer1" /> + <defs + id="defs2" /> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(42.607488,92.780029)"> + <g + id="g15861" + transform="translate(34.913169,-156.35674)"> + <circle + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.135684;stroke-opacity:1" + id="path4342" + cx="134.07271" + cy="173.43787" + r="7.5602813" /> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="130.28389" + y="177.21611" + id="text5886"><tspan + sodipodi:role="line" + id="tspan5884" + style="stroke-width:0.264583" + x="130.28389" + y="177.21611">K</tspan></text> + </g> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="82.876038" + y="-80.315704" + id="text7454"><tspan + sodipodi:role="line" + id="tspan7452" + style="stroke-width:0.264583" + x="82.876038" + y="-80.315704">A</tspan></text> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="31.88768" + y="-43.87215" + id="text7898"><tspan + sodipodi:role="line" + id="tspan7896" + style="stroke-width:0.264583" + x="31.88768" + y="-43.87215">B</tspan></text> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="153.94002" + y="-42.981239" + id="text8364"><tspan + sodipodi:role="line" + id="tspan8362" + style="stroke-width:0.264583" + x="153.94002" + y="-42.981239">C</tspan></text> + <g + id="g15881" + transform="translate(27.630306,-93.695044)"> + <circle + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.135684;stroke-opacity:1" + id="path4342-3" + cx="-30.602505" + cy="64.403992" + r="7.5602813" /> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="-34.655907" + y="68.182236" + id="text9424"><tspan + sodipodi:role="line" + id="tspan9422" + style="stroke-width:0.264583" + x="-34.655907" + y="68.182236">D</tspan></text> + </g> + <g + id="g15886" + transform="translate(-17.328292,-87.382089)"> + <circle + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.135684;stroke-opacity:1" + id="path4342-5" + cx="9.5870228" + cy="93.993073" + r="7.5602813" /> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="6.4490747" + y="97.771317" + id="text10528"><tspan + sodipodi:role="line" + id="tspan10526" + style="stroke-width:0.264583" + x="6.4490747" + y="97.771317">E</tspan></text> + </g> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="48.897526" + y="-10.660181" + id="text11500"><tspan + sodipodi:role="line" + id="tspan11498" + style="stroke-width:0.264583" + x="48.897526" + y="-10.660181">F</tspan></text> + <g + id="g15876" + transform="translate(43.811753,-136.32377)"> + <circle + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.135684;stroke-opacity:1" + id="path4342-35" + cx="82.657463" + cy="126.26711" + r="7.5602813" /> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="78.873932" + y="130.04535" + id="text12010"><tspan + sodipodi:role="line" + id="tspan12008" + style="stroke-width:0.264583" + x="78.873932" + y="130.04535">G</tspan></text> + </g> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="161.13219" + y="-9.7197342" + id="text12520"><tspan + sodipodi:role="line" + id="tspan12518" + style="stroke-width:0.264583" + x="161.13219" + y="-9.7197342">H</tspan></text> + <g + id="g15871" + transform="translate(-74.105386,-114.48369)"> + <circle + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.135684;stroke-opacity:1" + id="path4342-7" + cx="112.37273" + cy="116.71143" + r="7.5602813" /> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="110.58415" + y="120.48967" + id="text12920"><tspan + sodipodi:role="line" + id="tspan12918" + style="stroke-width:0.264583" + x="110.58415" + y="120.48967">I</tspan></text> + </g> + <g + id="g15866" + transform="translate(-11.45929,-129.96299)"> + <circle + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.135684;stroke-opacity:1" + id="path4342-6" + cx="72.357376" + cy="143.44485" + r="7.5602813" /> + <text + xml:space="preserve" + style="font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;stroke-width:0.264583" + x="71.807045" + y="146.21768" + id="text14090"><tspan + sodipodi:role="line" + id="tspan14088" + style="stroke-width:0.264583" + x="71.807045" + y="146.21768">J</tspan></text> + </g> + <ellipse + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-opacity:1" + id="path15910" + cx="90.187706" + cy="-1.3095872" + rx="132.6629" + ry="91.33815" /> + <ellipse + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-opacity:1" + id="path16014" + cx="29.470457" + cy="-6.0036306" + rx="53.721966" + ry="51.006378" /> + <ellipse + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-opacity:1" + id="path16014-6" + cx="154.85226" + cy="-4.0077286" + rx="53.721966" + ry="51.006378" /> + <ellipse + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-opacity:1" + id="path16340" + cx="49.329731" + cy="5.2435174" + rx="26.745842" + ry="26.28573" /> + <ellipse + style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-opacity:1" + id="path16340-2" + cx="167.58054" + cy="4.8638148" + rx="26.745842" + ry="26.28573" /> + </g> +</svg>