diff --git a/report/report.qmd b/report/report.qmd index caf5683266aff895a34068b1e73a56afd65e93e1..3b704f9c632aceda79d15817919ab63d23a7eb6f 100644 --- a/report/report.qmd +++ b/report/report.qmd @@ -230,3 +230,142 @@ le résultat de la DCT, de son inverse, de la DCT quantifiée et finalement de l'inverse quantifiée. # Huffman Adaptatif + +Dans ce travail pratique nous allons implémenter l'algorithme de compression +Huffman Adaptatif. Celui-ci aura pour but de construire un arbre binaire où +chaque noeud **externe** représentera un caractère dans la chaîne fournie pour +l'encodage et la valeur des noeuds **internes** correspondra à la somme des +poids de ses enfants (i.e. nombre d'occurrence du caractère). + +## Structure de l'arbre + +Pour la structure de l'arbre binaire, nous avons privilégié l'utilisation des +`dataclass` en `python` qui peuvent s'apparenter à de simples structures tels +qu'elles existent en `C`. Ceci nous permet d'éviter d'écrire des constructeurs +et des méthodes spécifiques à cette classe. Il est possible de se rendre compte +que la structure est bel et bien récursive du fait que les champs `left` et +`right` sont des références vers le même type `Node`. + +```python +@dataclass +class Node: + weight: int = 0 + nyt_code: str | None = None + value: str = "NYT" + left: Node | None = None + right: Node | None = None +``` + +## Encodage + +Dans la phase d'encodage, le but sera donc de construire itérativement l'arbre +en y insérant au fur et à mesure les caractères rencontrés dans la chaîne +fournie à la fonction d'encodage. Celle-ci est présentée ci-dessous : + +```python +def encode(input: str) -> str: + tree = Node() + encoded: list[str] = [] + seen: set[str] = set() + + for c in input: + insert_char(tree, c) + prefix = find_char(tree, c) + + if c not in seen: + seen.add(c) + encoded.append(prefix.nyt_code) + encoded.append(compute_code(c)) + else: + encoded.append(compute_prefix(tree, c)) + pprint.pp(tree) + + pprint.pp(tree) + + print(f'Nombre de swaps: {swap_count}') + + return ' '.join(encoded)[1:] +``` + +Nous allons donc commencer par insérer dans l'arbre le nouveau caractère +rencontré via la fonction `insert_char`. Suite à cela nous ferons une recherche +dans l'arbre pour calculer la partie NYT (_Not Yet Transmitted_) qui servira +de préfixe au code fixe du caractère. Si ce caractère n'a pas encore été +rencontré, il sera aussi inséré dans une structure de données de type `set` qui +permet de stocker des données sans doublons. Ceci est critique du fait que +le code NYT établi à l'insertion initiale du caractère a possiblement changé +suite aux divers _swaps_ qui ont possiblement eu lieu. Ces _swaps_ se produisent +lorsque le poids du sous-arbre gauche est supérieur à celui de droite. Par +conséquent, si nous allons insérer à nouveau un caractère connu au préalable +(i.e. présent dans le `set`), nous allons recalculer le préfixe NYT du caractère +via la fonction `compute_prefix` en parcourant l'arbre jusqu'à retrouvé ce +caractère. + +### Résultat de l'encodage + +Pour la chaîne de caractères "darkvador", le résultat de l'encodage sera le +suivant : + +```bash +Nombre de swaps: 4 +Code final: 000011 0 000000 00 01101 100 00110 1100 10001 10 0 11100 01010 110 + +``` + +Ci-dessous, le lecteur peut observer l'arbre qui fut construit dynamiquement au +moment de l'encodage. Il est nécessaire de mentionner le fait que le champ +`value` des noeuds internes est égale à `NYT` du fait que c'est la valeur par +défaut d'un noeud de type `Node`, cependant le **vrai** noeud NYT est celui dont +la valeur du champ `weight` est égale à 0. Celui-ci est donc aussi le noeud +terminal de l'arbre. + +```bash +Node(weight=9, + nyt_code=None, + value='NYT', + left=Node(weight=2, nyt_code='', value='d', left=None, right=None), + right=Node(weight=7, + nyt_code=None, + value='NYT', + left=Node(weight=2, + nyt_code='0', + value='a', + left=None, + right=None), + right=Node(weight=5, + nyt_code=None, + value='NYT', + left=Node(weight=2, + nyt_code='00', + value='r', + left=None, + right=None), + right=Node(weight=3, + nyt_code=None, + value='NYT', + left=Node(weight=1, + nyt_code='100', + value='k', + left=None, + right=None), + right=Node(weight=2, + nyt_code=None, + value='NYT', + left=Node(weight=1, + nyt_code=None, + value='NYT', + left=Node(weight=0, + nyt_code=None, + value='NYT', + left=None, + right=None), + right=Node(weight=1, + nyt_code='11100', + value='o', + left=None, + right=None)), + right=Node(weight=1, + nyt_code='1100', + value='v', + left=None, +```