RICM3 - 2010/2011 Code de Huffman 1 Introduction : codage ASCII
Transcription
RICM3 - 2010/2011 Code de Huffman 1 Introduction : codage ASCII
RICM3 - 2010/2011 Langage et Programmation 2, TP2 Code de Huffman Il est fortement conseillé de tester vos fonctions avec des entrées judicieusement choisies. Objectifs : Construire et utiliser des arbres de Huffman afin de coder efficacement des messages. 1 Introduction : codage ASCII vs codage de Huffman Tout type d’information est représenté par une suite de bits, 0 ou 1. En particulier la table ASCII permet de coder l’alphabet latin et bien d’autres caractères (256 en tout) sur 8 bits. Ce codage est dit de longueur fixe car chaque caractère, une fois codé, a la même longueur. Par exemple, le code de ’a’ est 01100001. On présente également souvent le code interprété comme un nombre en base 2, ce qui dans le cas de ’a’ donne 1 × 26 + 1 × 25 + 1 × 20 = 97. Il existe également des codes à longueur variable, comme le code de Huffman qui est l’objet de ce TP. Dans ce cas (comme pour le Morse), les caractères les plus fréquents sont codés de manière plus concise que ceux qui sont rares. Par exemple, en Morse la lettre E est codée par un ‘.’ (un seul symbole) alors que la lettre Y est codée par ‘-.--’ (4 symboles). Ainsi, le code d’un message quelconque sera en moyenne plus court que si l’on avait utilisé un code de longueur fixe. Mais cela introduit une problématique nouvelle : puisque la longueur des codes des différents caractères n’est pas toujours la même, comment déterminer où s’arrête le code d’un caractère et où commence le code du suivant ? Le code de Huffman répond à cette question grâce à la propriété suivante : le code d’un caractère n’est jamais le préfixe du code d’un autre caractère (on dit que le code de Huffman est un code préfixe). Le code de Huffman repose sur la construction et l’utilisation d’un arbre de Huffman qui est soit une feuille, soit un nœud auquel on associe un entier et deux arbres appelés fils. Les nœuds pères sont reliés à leurs nœuds fils par une arête. La racine de l’arbre est l’unique nœud ne possédant pas de parent. La hauteur d’un arbre est la plus grande distance en nombre d’arêtes de la racine à une feuille. Un arbre de Huffman est construit en se basant sur la fréquence de chaque caractère dans le message à coder, et indique comment coder ce message. On appelle fréquence d’un caractère dans un texte le nombre d’occurrences de ce caractère dans le texte considéré. Les caractères à coder sont portés par les feuilles de l’arbre et le code associé à un caractère est déterminé à partir du chemin entre la racine et la feuille portant ce caractère. Voici par exemple un arbre de Huffman servant au codage du message “hello leo”. /--(2,’o’) /--4 | \--(2,’e’) --9 | /--(3,’l’) \--5 | /--(1,’ ’) \--2 \--(1,’h’) À chaque nœud est associé un poids, qui est la somme des fréquences des caractères portés par les feuilles qui dépendent de ce nœud. Quand on parle du poids d’un arbre, on fait référence au poids de sa racine. On convient que la racine est en haut d’un arbre et que la branche de gauche d’un arbre (en bas sur le dessin ) correspond à un 0 et celle de droite à un 1. Ainsi la lettre ’h’ est codée par 000 tandis que la lettre ’e’ est codée par 10. Le message entier, “hello leo”, sera codé par 00010010111001011011. Remarques : il existe plusieurs arbres de Huffman équivalents pour un même texte. Par exemple, échanger dans l’arbre ci-dessus les feuilles contenant les lettres ’o’ et ’e’ donne un arbre de Huffman équivalent pour ce texte. D’autre part, les étiquettes portées par les nœuds ne servent qu’à la construction de l’arbre. Lors du codage et du décodage d’un texte, seules les informations contenues par les feuilles sont utilisées. Exercice 1 (1 point) – Quelle taille a le code ASCII du message “hello leo” ? Comparer à la taille du codage de Huffman de ce même message. – En utlisant l’arbre de de Huffman associé à “hello leo”, coder les mots “lolo” et “ole ole”. – Pour l’arbre fourni en exemple, de quelle longueur diffèrent les deux codages (séquences de bits) les plus longs d’un code de Huffman pour une lettre ? – Combien de bits ces deux codages ont-ils en commun ? 2 Manipulation d’un arbre de Huffman Les fichiers affichage.cmi et affichage.cmo, disponibles sur le site de l’UE (http: //www-verimag.imag.fr/˜plafourc/teaching/LP2_RICM3_2010_2011.php). Ces fichiers fournissent le type arbre de Huffman et une fonction afficher_arbre qui permet d’afficher de tels arbres. Le type arbre de Huffman défini est le suivant : type arbreHuffman = Feuille of int * char | Noeud of arbreHuffman * int * arbreHuffman Nous reviendrons dans le TP3 sur la compilation de programmes CAML. Noter pour l’instant qu’un fichier .cmo contient le bytecode (code intermédiaire portable) correspondant à la compilation d’un fichier .ml, tandis qu’un fichier .cmi rend visibles toutes les définitions décrites dans un fichier d’interface .mli (par défaut, toutes les définitions du fichier .ml). Pour pouvoir les utiliser, copier affichage.cmi et affichage.cmo dans votre répertoire courant et rajouter en entête de votre fichier tp2.ml les commandes #load "affichage.cmo" puis open Affichage. Exercice 2 (0,5 point) Définir le type foret en représentant une forêt par une liste d’arbres de Huffman. Exercice 3 (0,5 point) Construire et afficher l’arbre présenté en introduction. 2 2.1 Décodage d’un message On définit le type bit de la façon suivante : type bit = Zero | Un Exercice 4 (2 points) Écrire la fonction decodage, qui prend en entrée un arbre de Huffman et un message sous forme de liste de bits et qui rend le message décodé sous forme de chaı̂ne de caractères. Pour cela vous aurez besoin des primitives fournies par CAML, ˆ qui concaténe deux chaı̂nes strings et String.make 1 c qui construit la chaı̂ne “c”1 . 2.2 Codage d’un message À partir d’un arbre de Huffman, pour connaı̂tre le code d’un caractère, il faut parcourir l’arbre en recherchant quelle feuille porte le caractère que l’on veut coder et quel est le chemin qui y mène. Cela n’est pas réaliste pour coder un message. Nous allons donc construire à partir d’un arbre de Huffman une liste d’associations entre caractère et code, en utilisant les fonctions offertes par le module List de CAML (cf. documentation). Exercice 5 (1 points) Écrire la fonction associer qui prend un arbre de Huffman et qui rend une liste de couples (caractère, code associé). Exercice 6 (3 points) En créant et en utilisant une liste d’associations comme ci-dessus, écrire la fonction de codage qui prend un arbre de Huffman et un texte et qui rend le texte codé par l’arbre donné. Utiliser les primitives suivantes : – String.length s donne la longueur de la chaı̂ne s. – s.[k], pour s une chaı̂ne donnée et k un entier entre 0 et (String.length s - 1), rend le (k + 1)-ème élément de s (l’indiçage commence à 0). 3 Construction des arbres de Huffman Pour construire l’arbre de Huffman correspondant à un texte, on procède de la manière suivante : – On calcule la fréquence de chaque caractère présent dans le texte. – On crée une forêt contenant un arbre pour chaque caractère présent dans le texte. Chaque arbre est constitué d’une unique feuille portant le caractère traité et son poids. La forêt doit être triée par poids croissant des arbres. – On fusionne les deux feuilles ayant le poids le plus petit de telle sorte que l’arbre obtenu ait pour sous-arbres les deux feuilles fusionnées et pour racine un nœud de poids la somme des poids des feuilles. On insère cet arbre dans la forêt de sorte qu’elle reste triée. On prend soin d’insérer l’arbre le plus loin possible dans la forêt car cela permettra d’obtenir à la fin un arbre le plus équilibré possible. – On recommence avec les deux arbres qui sont désormais de poids minimal, et on itère ce processus jusqu’à ce qu’il n’y ait plus qu’un seul arbre dans la forêt. Cet arbre est l’arbre de Huffman recherché. Ainsi, sur l’exemple, la construction de l’arbre de Huffman s’effectuera de la façon suivante : 1 Pour plus de précisions référez-vous à la documentation en ligne http://caml.inria.fr dans « resources » suivre « Objective Caml manual » puis « The core language » 3 Étape 1 : [--(1,’h’); --(1,’ ’); --(2,’e’); --(2,’o’); --(3,’l’)] Étape 2 : /--(1,’ ’) [--(2,’e’); --(2,’o’); --2 ; --(3,’l’)] \--(1,’h’) Étape 3 : /--(1,’ ’) /--(2,’o’) [--2 ; --(3,’l’); --4 ] \--(1,’h’) \--(2,’e’) Étape 4 : /--(2,’o’) /--(3,’l’) [--4 ; --5 ] \--(2,’e’) | /--(1,’ ’) \--2 \--(1,’h’) Étape 5 : /--(2,’o’) /--4 | \--(2,’e’) [--9 ] | /--(3,’l’) \--5 | /--(1,’ ’) \--2 \--(1,’h’) Sur le site web, on donne les fichiers de la fonction frequence qui prend en entrée un texte et retourne une liste d’associations représentant chaque caractère associé à sa fréquence dans le texte. Exercice 7 (0,5 point) – Donner l’arbre de Huffman associé à “babar” et le codage de “raba”. – Si toutes les fréquences sont égales, quelle sera la hauteur de l’arbre de Huffman correspondant ? Exercice 8 (1 point) – Écrire une fonction poids qui vous aidera à répondre à la question cidessous. – Adapter la fonction du TP1 inserer dans une liste triée pour qu’elle manipule des listes d’arbres. L’ordre utilisé sera l’ordre sur le poids des racines des arbres considérés. L’arbre à insérer le sera le plus loin possible dans la liste (dans le cas d’une égalité). Exercice 9 (0,5 point) En utilisant inserer, créer la forêt initiale à partir d’une liste d’associations indiquant les fréquences de chaque caractère. 4 Exercice 10 (3 points) Écrire la fonction fusion qui fusionne deux arbres comme indiqué cidessus. Écrire la fonction huffman qui prend un texte et rend l’arbre de Huffman correspondant. Exercice 11 (0,5 point) Le fichier lp2_code.ml sur la page web du cours contient un arbre de Huffman et un message sous forme de liste de bits. Décoder le message. Comme vous pouvez le remarquer, pour décoder un message on a besoin de l’arbre ayant servi à son codage. Il faudra donc trouver un moyen d’exprimer cet arbre par une suite de bits compacte (notre objectif est de compresser un message) pour pouvoir le transmettre en même temps que notre message, ce qui n’est pas le cas pour un message codé en ASCII ou morse. Ce problème n’est pas traité dans ce TP mais il existe sur internet de nombreuses références à ce sujet. 4 Transformation BWT Pour un mot w sur un alphabet A on définit sa rotation r(w) par : ε si w = ε, r(w) = w0 a si w = aw0 avec a ∈ A, w0 ∈ A∗ . L’opération r effectue une rotation du mot en déplaçant la lettre initiale en fin de mot. Pour un mot w de taille n on appelle w, r(w), r(r(w)), . . . , rn−1 (w) les rotations de w. La transformation BWT considère les n rotations possibles du mot w, les trie, et renvoie le mot formé des dernières lettres des rotations triées, ainsi que la position de la première occurence du mot initial dans la liste triée de ses rotations. Une description plus complète est donnée sur la page http://en.wikipedia.org/wiki/BWT Exercice 12 (2,5 points) Écrire une fonction rotations qui pour un mot de taille n quelconque calcule la liste de ses n rotations (non nécessairement distinctes). Exercice 13 (1 points) Écrire une fonction bwt qui calcule la transformée BWT d’un mot. Exercice 14 (3 points) Écrire une fonction bwti qui calcule la transformée BWT inverse. 5 Transformation MTF (Bonus) La transformation MTF (pour Move To Front) code un mot sur un alphabet quelconque de taille n en une suite d’entiers entre 0 et n − 1. La procédure est décrite à la page suivante : http://en.wikipedia.org/wiki/Move-to-front_transform Exercice 15 (2 points) Écrire une fonction mtf calculant la transformée MTF d’une chaı̂ne. Vous pourrez utiliser Char.code et Char.chr. 5 Exercice 16 (1 points) Écrire une fonction mtfi calculant la transformée MTF inverse. Exercice 17 (1 points) Écrire une fonction qui pour une chaı̂ne calcule sa transformée BWT, puis MTF, applique le codage de Huffman, et retourne tout ce qui est nécessaire pour calculer le décodage inverse. 6