Algorithme de compression de Burrows-Wheeler Sujet proposé par Jean-Pierre Tillich email : jean-pierre.tillich@inria.fr Difficulté : ** (moyenne) 1 Quelques mots sur le sujet Le but de ce projet est de réaliser et de comprendre comment fonctionne l algorithme de compression bzip2 ainsi que l algorithme de décompression bunzip2 utilisés fréquemment en pratique et qui donnent souvent des taux de compression meilleurs que l algorithme de Lempel-Ziv (utilisé dans zip, gzip par exemple). Cet algorithme de compression fait appel à de nombreuses transformations : transformation de Burrows-Wheeler, algorithme de compression de Huffman adaptatif, transformation move-to-front et fournit un bon panorama de différentes techniques de compression. Certaines de ces transformations comme la transformation move-to-front sont très simples à implémenter, d autres sont en revanche plus délicates à mettre en oeuvre si l on veut une implémentation efficace. Ainsi, bien que la transformation de Burrows-Wheeler revient essentiellement à implémenter un tri, le caractère spécifique des chaînes à trier requiert un traitement spécifique si l on veut une bonne vitesse de compression sur tous les types de texte. On verra notamment comment mettre à profit ici les techniques de tri à base d arbres suffixes. De manière générale, l algorithme de compression de Burrows Wheeler fournit une bonne illustration de l intérêt d utiliser diverses techniques algorithmiques sur les arbres pour obtenir une implémentation efficace. 2 Quelques généralités sur la compression Le texte à compresser sera vu comme une suite x 0 x 1... x n 1 de symboles pris dans un alphabet A. Un algorithme de compression (sans perte) consiste à calculer une suite binaire z 0, z 1,... z m 1 à partir de la suite précédente de telle manière à ce qu il soit possible de retrouver la suite x 0 x 1... x n 1 d origine à partir de la seule suite z 0 z 1... z n 1. Le taux de compression est défini par le rapport m log 2 A n (ici A désigne le cardinal de l alphabet). Il vous est conseillé de choisir A = {0, 1} 8 (cela reviendra donc à lire votre texte octet par octet). 3 L algorithme de compression de Huffman adaptatif On commencera ce projet par l implémentation d un algorithme de compression assez rudimentaire qui est souvent utilisé, c est l algorithme de Huffman adaptatif. On appelle A = {0, 1} 8 l alphabet du texte. Soit x 0... x n 1 une suite de n symboles de A que l on veut compresser. L algorithme de compression de Huffman adaptatif code chacun des x i par une séquence binaire y i = y 1 i y2 i... yli i où les y j i sont des bits et l i représente la longueur binaire du codage du symbole x i. La séquence compressée est alors la concaténation des mots binaires y i : y 0... y n 1. Il se peut que certaines des longueurs l i soient supérieures à log 2 A, mais la longueur moyenne de ces l i est elle en général inférieure ou égale à cette quantité. Le texte est donc effectivement compressé. En substance, l idée est de coder les symboles fréquents avec des chaînes binaires courtes alors que les lettres peu fréquentes sont codées avec des chaînes plus longues. L algorithme est dit adaptatif car il calcule à chaque instant t les fréquences d apparition de chaque 1
symbole de A dans la suite d octets x 0... x t. En fonction de ces fréquences il calcule le mot binaire y t optimal par lequel on va coder x t. On utilise pour cela les arbres de Huffman. 3.1 Le codage de Huffman (non adaptatif) Pour expliquer l algorithme de Huffman adaptatif, considérons d abord l algorithme de Huffman non adaptatif. Pour cela, nous supposons que nous connaissons pour chaque symbole a de l alphabet A sa fréquence d apparition f(a). Cela résulte soit d un modèle préalable pour le type de fichier à compresser ou par un calcul des fréquences d apparition de chaque symbole dans le texte à compresser. Le codage de Huffman (non adaptatif) consiste à calculer dans un premier temps un arbre binaire dont les feuilles sont associées aux symboles de A. On appelle cet arbre l arbre de Huffman associé à l ensemble {(a, f(a)), a A}. On utilise ensuite cet arbre pour coder un symbole a en considérant l unique chemin menant de la racine de l arbre binaire à la feuille de l arbre associé à a. Ce chemin peut se coder par une suite de 0 et 1, 0 indiquant que l on emprunte le fils gauche, 1 le fils droit. Ainsi une feuille à laquelle on peut accéder en empruntant d abord le fils gauche de la racine, puis le fils droit, puis le fils droit de ce fils droit se codera par la suite binaire 011. A titre d exemple, voici un arbre obtenu pour un alphabet de 4 symboles, A = {a, b, c, d} et les codages binaires correspondant. a : 0 a b c d b : 10 c : 110 d : 111 L intérêt du codage de Huffman est de minimiser la longueur moyenne l du codage binaire par symbole (parmi l ensemble de tous les codages binaires dont on peut inverser le codage, c est à dire retrouver la séquence de symboles d origine x 0... x n 1 à partir de la concaténation des mots binaires y i, y i représentant le codage du symbole x i ), c est à dire l def = a A f(a)l(a) où l(a) est la longueur binaire du codage du symbole a. L arbre binaire utilisé pour le codage de Huffman est obtenu par la procédure suivante décrite dans l algorithme 1. L arbre binaire de l exemple précédent aurait été ainsi obtenu si les fréquences avaient été les suivantes : f(a) = 0.5, f(b) = 0.25, f(c) = 0.15, f(d) = 0.1. 1 Décrire brièvement une implémentation efficace de cet algorithme. Quelle est sa com- Question plexité? 3.2 L algorithme de Huffman adaptatif Un des inconvénients majeurs de l algorithme de compression de Huffman est de nécessiter d inclure dans le fichier décompressé une description de l arbre binaire utilisé pour le codage ou de la table de codage associée. En effet, à la décompression, on ne connait pas a priori les fréquences des symboles du texte à compresser (alors qu à la compression, même sans modèle préalable pour le texte ces fréquences peuvent être calculées à partir du texte lui-même). On n est donc pas capable de reconstituer l arbre 2
Algorithme 1 Huffman (non adaptatif) INPUT: {(a, f(a) : a A} OUTPUT: l arbre binaire T utilisé pour le codage de Huffman E for a in A do Construire un arbre t(a) réduit à une racine portant deux informations, le symbole a et la fréquence f(a). E E {t(a)} while E > 1 do extraire (et supprimer) de E les deux arbres t 1 et t 2 dont les racines portent la fréquence la plus petite Construire un arbre t ayant une racine de fréquence f 1 + f 2 où f i est la féquence portée par la racine de t i. Rajouter t à E return l unique arbre dans E binaire sous-jacent à partir de la seule concaténation des codages binaires y i des symboles x i du texte à compresser. Dans ce cas, il serait donc impossible de reconstituer la suite de symboles d origine. Il existe un moyen de contourner ce problème au moyen de l algorithme de Huffman adaptatif. Voyons d abord comment effectuer le codage. Huffman adaptatif Codage. Supposons que nous ayons déja lu t caractères dans le texte, correspondant à K symboles distincts. Soit {a 0,..., a K 1 } ces symboles. soit f k la fréquence d apparition de a k dans la séquence à compresser x 0... x t 1. Nous considérons également un symbole fourretout a K auquel on attribue une fréquence nulle : f K = 0. Nous calculons l arbre de Huffman T t associé à l ensemble {(a i, f i ), 0 i K}. le t + 1-ème symbole est lu et est codée par son mot de code s il existe, par le K + 1-ème mot de code suivi du code ascii ou unicode du symbole sinon. Huffman adaptatif Décodage. L arbre initial est constitué d une unique feuille, celle du symbole fourre-tout. Jusqu à épuisement, on parcourt l arbre en lisant les bits du texte codé ( 0 à gauche, 1 à droite) jusqu à arriver à une feuille s il n est pas associé au symbole fourre-tout on imprime le symbole correspondant, on met à jour l arbre, sinon, il s agit du symbole fourre-tout on lit les 8 bits suivants pour obtenir le code ascii d une lettre que l on imprime on ajoute une feuille dans l arbre, on met à jour l arbre. L arbre de Huffman est donc amener à changer à chaque instant. Néanmoins, les fréquences ne changent que très peu après un certain temps entre deux instants consécutifs. On s attend donc à ce que T t et T t+1 soient très proches l un de l autre. Question 2 Proposer un moyen efficace de mettre à jour l arbre de Huffman T t pour obtenir T t+1. Quelle est la complexité de votre algorithme? Implémenter l algorithme de compression et décompression associé et mesurez le taux de compression sur différentes types de fichier. 4 La transformation de Burrows-Wheeler La transformation de Burrows-Wheeler n est pas un algorithme de compression. Son intérêt est de réaliser une transformation sur la suite x 0... x n 1 à compresser qui va permettre d améliorer le taux de compression de l algorithme de Huffman qui est utilisé ultérieurement. En substance elle associe au texte une certaine permutation z 1... z n de ce dernier et un indice compris entre 0 et n 1. Ainsi, le 3
texte après transformation est même légèrement plus long que le texte initial. En revanche, il est sous une forme qui va permettre d exploiter de manière significative des répétitions de motif qui se produisent dans ce dernier. Des répétitions de motif dans ce dernier conduisent en effet à des répétitions de symboles identiques dans la séquence z 1... z n. Une très bonne introduction à cette transformation est donnée dans l article http://www.dogma.net/markn/articles/bwt/bwt.htm. Question 3 Lire cet article et implémenter la transformation de Burrows-Wheeler ainsi que la transformation inverse de manière naïve. Question 4 Quelle est la complexité de votre implémentation dans le pire des cas si l on compte une comparaison entre octets de manière unitaire? Question 5 Lire maintenant l article de Burrows et Wheeler [1] (et notamment la section 4) qui se trouve sur http://www.hpl.hp.com/techreports/compaq-dec/src-rr-124.pdf. On consultera également l article de McCreight de construction d un arbre suffixe. Question 6 Expliquer comment fonctionne l algorithme Q de la section 4 dans l article de Burrows- Wheeler. Quelle est sa complexité dans le pire des cas? Implémenter l algorithme Q ainsi que l algorithme à base d arbres suffixes de McCreight [2]. Comparer vos implémentations sur différents exemples de texte. Question 7 Proposer éventuellement des améliorations à l algorithme Q. 5 La transformation Move to front Question 8 Implémenter l algorithme de compression de texte consistant à effectuer dans un premier temps la transformation de Burrows-Wheeler puis à appliquer l algorithme de Huffman adaptatif. Essayez cet algorithme sur plusieurs types de fichier texte. Améliorez-vous le taux de compression du texte par rapport à la méthode consistant à utiliser uniquement l algorithme de Huffman adaptatif? Proposer une explication de ce phénomène. Comme cela a été brièvement expliqué dans la section 4 (et expliqué plus en détail dans l article [1]), la transformation de Burrows-Wheeler conduit naturellement à une permutation z 1... z n du texte initial comportant des plages de symboles identiques. Un des moyens d exploiter cette structure est de transformer la suite d octets z 1... z n en une suite d entiers positifs u 1... u n par la transformation move-to-front. Le principe en est extrêmement simple. La transformation Move-to-front consiste tout simplement à effectuer la transformation suivante A titre d illustration, si le tableau Y est constitué Algorithme 2 Move-to-Front INPUT: z 0... z n 1 OUTPUT: u 0... u n 1 Y tableau où l on placé tous les caractères de l alphabet du texte dans un ordre prédéfini. for i = 0 to n 1 do u i nombre de caractères précédant le caractère z i dans le tableau Y placer le caractère z i en tête du tableau Y en décalant les autres caractères du tableau vers la droite par [ a,, b, c, r ] et que la suite z est donnée par la chaîne de caractères caraab, la suite u produite est égale à 2, 1, 3, 1, 0, 3. Si la suite z 0... z n 1 contient de nombreuses rafales de symboles identiques, on s attend notamment à ce que le 0 apparaisse très souvent dans la séquence produite. Question 9 Implémenter la transformation move-to-front, ainsi que la transformation inverse. Question 10 Mesurer le taux de compression sur différents types de fichier obtenus en effectuant d abord la transformation de Burrows-Wheeler, puis la transformation Move-to-front et enfin l algorithme de Huffman. 4
6 Coder de larges plages de zéros La transformation move-to-front appliquée à la sortie de l algorithme de Burrows-Wheeler conduit fréquemment à de longues plages de zéro. Question 11 Proposer une méthode pour coder des plages de zéro qui permettrait d améliorer le taux de compression obtenue dans la question précédente en appliquant maintenant la séquence de transformations suivantes au fichier à compresser : transformation de Burrows-Wheeler, puis transformation Move-to-front, codage des plages de zéro et enfin algorithme de Huffman. Références [1] M. Burrows, D. Wheeler, A block sorting lossless data compression algorithm, Technical Report 124, Digital Equipment Corporation, http://www.hpl.hp.com/techreports/compaq-dec/src-rr-124.pdf. [2] E.M. McCreight, A space economical suffix tree construction algorithm, Journal of the ACM, Vol. 32, No. 2, Avril 1976, p. 262-272. 5