69 votes

Le meilleur algorithme de Compression d'une séquence d'entiers

J'ai un grand tableau avec une fourchette de nombres entiers qui sont pour la plupart en continu, par exemple, 1-100, 110-160, etc. Tous les nombres entiers positifs. Quel serait le meilleur algorithme pour compresser ce?

J'ai essayé de le dégonfler algorithme, mais qui me donne seulement 50% de compression. Notez que l'algorithme ne peut pas être figé.

Tous les numéros sont uniques et à augmenter progressivement.

Aussi, si vous pouvez m'indiquer la java de la mise en œuvre d'un tel algorithme, ce serait super.

81voto

Daniel Lemire Points 812

J'ai écrit un récent document de recherche que les enquêtes sur les meilleurs régimes pour ce problème. Veuillez voir:

Daniel Lemire et Leonid Boytsov, le Décodage des milliards d'entiers par seconde grâce à la vectorisation, Logiciel de Pratique et de l'Expérience (à paraître) http://arxiv.org/abs/1209.2137

Il comprend une vaste évaluation expérimentale.

Vous pouvez trouver une mise en œuvre complète de toutes les techniques en C++11 en ligne: https://github.com/lemire/FastPFor

Si vous préférez Java, veuillez consulter https://github.com/lemire/JavaFastPFOR

38voto

CesarB Points 18048

Tout d'abord, préparer votre liste de valeurs en prenant la différence entre chaque valeur et la précédente (pour la première valeur, supposons que le précédent était de zéro). Cela devrait, dans votre cas, donner surtout une séquence de celles qui peuvent être compressés beaucoup plus facilement par la plupart des algorithmes de compression.

De cette façon, le format PNG ne pour améliorer sa compression (ce n'est l'un de plusieurs la différence des méthodes de suivi par le même algorithme de compression utilisé par gzip).

18voto

Tamir Points 1314

Eh bien, je vais voter de façon plus intelligente. Tout ce que vous avez à stocker est [int:startnumber][int/byte/quoi que ce soit:nombre d'itérations] dans ce cas, vous devez activer votre exemple de tableau dans 4xInt valeur. Après cela, vous pouvez compresser le comme vous voulez :)

17voto

brianegge Points 12857

Alors que vous pourriez concevoir un algorithme spécifique à votre flux de données, il est probablement plus facile d'utiliser un rayon algorithme de codage. J'ai couru quelques tests d'algorithmes de compression disponible en Java et constaté des taux de compression pour une séquence de un million de nombres entiers consécutifs:

None        1.0
Deflate     0.50
Filtered    0.34
BZip2       0.11
Lzma        0.06

12voto

Marc Gravell Points 482669

De quelle taille sont les chiffres? En plus des autres réponses, vous pourriez envisager de base-128 variante de codage par longueur, qui vous permet de stocker des nombres plus petits dans les octets tout en permettant à un plus grand nombre. Le MSB signifie "il y a un autre octet" - ce qui est décrit ici.

Combinez cela avec les autres techniques de sorte que vous stockez à ignorer "taille", "prendre la taille", "sauter" taille", "prendre la taille" mais de noter que ni le "skip", ni de "prendre" ne sera jamais nulle, donc nous allons soustraire un de chaque (qui permet d'enregistrer un octet supplémentaire pour une poignée de valeurs)

Donc:

1-100, 110-160

est "skip 1" (à supposer commencer à zéro, car il rend les choses plus faciles), "prendre 100", "skip 9", "prendre 51"; soustrait 1 de chacun, de donner (comme les décimales)

0,99,8,50

qui encode (hex):

00 63 08 32

Si nous voulions passer/prendre un plus grand nombre de 300, par exemple; nous soustrayons 1 donnant 299 - mais qui remonte à plus de 7 bits; en commençant par le petit bout, nous coder les blocs de 7 bits et une ESM pour indiquer la suite:

299 = 100101100 = (in blocks of 7): 0000010 0101100

donc, en commençant par le petit bout:

1 0101100 (leading one since continuation)
0 0000010 (leading zero as no more)

donner:

AC 02

On peut donc coder un grand nombre facilement, mais un petit nombre (dont le son est typique pour passer/prendre) prennent moins d'espace.

Vous pourriez essayer d'exécuter ce par le biais de "dégonfler", mais il pourrait ne pas aider beaucoup plus...


Si vous ne voulez pas faire face à tout cela en désordre encodage cruff vous-même... si vous pouvez créer l'entier-le tableau des valeurs (0,99,8,60) - vous pouvez utiliser le protocole de tampons avec des paniers répété uint32/uint64 - et il va faire tout le travail pour vous ;-p

Je n'ai pas "faire" de Java, mais il y en a plein le C# de mise en œuvre (pour emprunter de l'encodage des bits à partir de mon protobuf-net du projet):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
static class Program
{
    static void Main()
    {
        var data = new List<int>();
        data.AddRange(Enumerable.Range(1, 100));
        data.AddRange(Enumerable.Range(110, 51));
        int[] arr = data.ToArray(), arr2;

        using (MemoryStream ms = new MemoryStream())
        {
            Encode(ms, arr);
            ShowRaw(ms.GetBuffer(), (int)ms.Length);
            ms.Position = 0; // rewind to read it...
            arr2 = Decode(ms);
        }
    }
    static void ShowRaw(byte[] buffer, int len)
    {
        for (int i = 0; i < len; i++)
        {
            Console.Write(buffer[i].ToString("X2"));
        }
        Console.WriteLine();
    }
    static int[] Decode(Stream stream)
    {
        var list = new List<int>();
        uint skip, take;
        int last = 0;
        while (TryDecodeUInt32(stream, out skip)
            && TryDecodeUInt32(stream, out take))
        {
            last += (int)skip+1;
            for(uint i = 0 ; i <= take ; i++) {
                list.Add(last++);
            }
        }
        return list.ToArray();
    }
    static int Encode(Stream stream, int[] data)
    {
        if (data.Length == 0) return 0;
        byte[] buffer = new byte[10];
        int last = -1, len = 0;
        for (int i = 0; i < data.Length; )
        {
            int gap = data[i] - 2 - last, size = 0;
            while (++i < data.Length && data[i] == data[i - 1] + 1) size++;
            last = data[i - 1];
            len += EncodeUInt32((uint)gap, buffer, stream)
                + EncodeUInt32((uint)size, buffer, stream);
        }
        return len;
    }
    public static int EncodeUInt32(uint value, byte[] buffer, Stream stream)
    {
        int count = 0, index = 0;
        do
        {
            buffer[index++] = (byte)((value & 0x7F) | 0x80);
            value >>= 7;
            count++;
        } while (value != 0);
        buffer[index - 1] &= 0x7F;
        stream.Write(buffer, 0, count);
        return count;
    }
    public static bool TryDecodeUInt32(Stream source, out uint value)
    {
        int b = source.ReadByte();
        if (b < 0)
        {
            value = 0;
            return false;
        }

        if ((b & 0x80) == 0)
        {
            // single-byte
            value = (uint)b;
            return true;
        }

        int shift = 7;

        value = (uint)(b & 0x7F);
        bool keepGoing;
        int i = 0;
        do
        {
            b = source.ReadByte();
            if (b < 0) throw new EndOfStreamException();
            i++;
            keepGoing = (b & 0x80) != 0;
            value |= ((uint)(b & 0x7F)) << shift;
            shift += 7;
        } while (keepGoing && i < 4);
        if (keepGoing && i == 4)
        {
            throw new OverflowException();
        }
        return true;
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X