Je lis l'article NJ Larsson, A. Moffat: Compression basée sur un dictionnaire hors ligne , qui décrit un algorithme de compression qui, si je le comprends bien, est assez similaire au codage par paire d'octets .
Étant donné une chaîne de longueur , j'essaie de comprendre comment on peut la compresser en temps linéaire avec cette méthode de compression. Comment cela se fait-il exactement? J'ai lu le document, mais je ne comprends toujours pas comment ils atteignent le temps linéaire, alors peut-être que je comprendrais qu'il soit expliqué d'une manière différente.
Ma première confusion survient dans la première étape de l'algorithme, où nous trouvons la paire la plus courante, par exemple dans abcababcabc
la paire la plus courante ab
serait remplacée par un nouveau symbole, disons XcXXcXc
. Je ne comprends pas comment trouver la paire la plus courante assez rapidement. Mon approche naïve serait de regarder d'abord la première paire ab
, puis de compter le nombre d'occurrences, puis de regarder la paire suivante bc
et de compter le nombre d'occurrences, etc. Cependant, cela donnerait déjà juste pour trouver la paire la plus courante une fois .
Ensuite, même si j'ai compris comment trouver la paire la plus courante en temps . Mon prochain problème est que, ne devons-nous pas trouver la paire la plus courante jusqu'à fois? Et donc cela donnerait un temps total de ?
Réponses:
J'ai vérifié le code, relu le document et il semble que l'algorithme ne fonctionne pas dans comme vous l'avez décrit. Les appels récursifs de recherche de paires fonctionnent dansO(n)
O(nlogn) divisé par une constante si vous souhaitez l'emballer jusqu'au bout. En fait, la structure en dessous garde les pointeurs vers la première occurrence de la paire, divise la file d'attente des paires par les occurrences pour la rendre efficace, mais toujours linéaire. D'autre part en analysant le code écrit par Ph.D. élève de l'auteur, j'ai découvert plusieurs astuces - la longueur des paires (le nom d'origine) est donnée comme paramètre (la valeur par défaut est 2) et la récurrence est limitée par le paramètre, qui peut rejeter d'autres appels (la valeur par défaut est d'environ 5 niveaux), avec possibilité de faire un seul passage ou pousser jusqu'à la fin. Le code par défaut s'exécute dans , mais ne produira pas de compression optimale. Il y a aussi un interrupteur pour préférer la mémoire, le temps ou être neutre.O(n)
Le papier et le code montrent l'utilisation de la table de hachage, qui ne fonctionne que dans le temps constant attendu, mais la fonction donnée fonctionne très bien avec les caractères. Le code limite également la longueur d'entrée par un codage en dur constant.
Changer la table de hachage en arbre de suffixes et réécrire la partie de la récurrence en paires partielles de bookkeepping pourrait donner de meilleurs résultats - je ne suis pas sûr que ce soit possible de le faire , ce serait probablement une question différente.O(n)
Il existe également une astuce utilisée pour empêcher l'espace vide de traverser - le début du bloc vide pointe vers le premier caractère non vide, mais cela accélère uniquement la traversée, toujours avec un temps log-linéaire. La partie responsable de ce comportement remplace les paires par le nouveau caractère à chaque itération - conserver uniquement les règles de réécriture et les utiliser serait plus rapide, mais l'accès pour vérifier si de nouvelles paires ont été introduites nécessiterait de vérifier les règles de réécriture par rapport aux nouvelles - dans le cas où nous construisons un arbre binaire complet avec des paires aux feuilles de telle manière qu'après avoir remplacé la paire par un nouveau caractère et concédé avec le parent, nous avons obtenu de nouvelles paires aux feuilles jusqu'à la racine - il y a caractères puis paires, puis ,n n2 n4 n8 , ... Vous voyez le modèle, il y a paires, mais selon la description de l'algorithme, cela prend prenant du temps uniquement les traversées d'entrée.n16 2n O(nlogn+n)
la source