Wie kann ein numerischer Bereich am effizientesten gespeichert werden?

29

Bei dieser Frage geht es darum, wie viele Bits zum Speichern eines Bereichs erforderlich sind. Oder anders ausgedrückt: Wie kann der maximale Bereich für eine bestimmte Anzahl von Bits gespeichert werden?

Stellen Sie sich vor, wir möchten einen Unterbereich zwischen 0 und 255 speichern.

Also zum Beispiel 45-74.

Wir können das obige Beispiel als zwei vorzeichenlose Bytes speichern, aber es fällt mir auf, dass es dort eine gewisse Redundanz der Informationen geben muss. Wir wissen, dass der zweite Wert größer als der erste ist. In dem Fall, dass der erste Wert groß ist, sind weniger Bits für den zweiten Wert erforderlich, und in dem Fall, dass der zweite Wert groß ist, sind weniger Bits für den ersten Wert erforderlich .

Ich vermute, dass jede Komprimierungstechnik zu einem geringfügigen Ergebnis führen würde. Daher ist es möglicherweise eine bessere Frage, sich die Frage zu stellen, wie hoch der maximale Bereich ist, der in einem Byte gespeichert werden kann. Dies sollte größer sein als das, was durch die getrennte Speicherung der beiden Zahlen erreicht werden kann.

Gibt es dafür Standardalgorithmen?

rghome
quelle
Müssen Sie auch den Start des Sortiments speichern?
Ewan
@Ewan Ich folge nicht wirklich. Im obigen Beispiel ist 45 der Anfang (das Minimum) und 74 das Ende (das Maximum) und beide müssen gespeichert werden.
Rghome
2
Das ist auch die Frage, wie viel Platz ein Typ benötigt, der einen beliebigen Bereich speichern kann. oder wie viel Platz benötigt ein Typ, der 45-74 speichern kann?
Ewan
1
Ich hoffe, dass Sie dies in realen Anwendungen nicht tun, obwohl es sicherlich gut ist, darüber nachzudenken. Der Grund dafür ist, dass die Komplexität realer Anwendungen so groß ist, dass wir weniger als 100% optimierten Code akzeptieren müssen. Aus diesem Grund gab es Compiler.
NoChance
3
@rghome, ich stimme zu, selbst die einfachste Anforderung erzeugt Hunderte von Codezeilen. Jeder ist fehleranfällig. Persönlich würde ich für Hardware bezahlen, als die Komplexität der Software zu erhöhen.
NoChance

Antworten:

58

Zählen Sie einfach die Anzahl der möglichen Bereiche. Es gibt 256 Bereiche mit Untergrenze 0 (0-0, 0-1, ... 0-254, 0-255), 255 Bereiche mit Untergrenze 1, ... und schließlich 1 Bereich mit Untergrenze 255 (255- 255). Die Gesamtzahl ist also (256 + 255 + ... + 1) = 257 * 128 = 32.896. Da dies etwas höher als 2 15 = 32.768 ist, benötigen Sie immer noch mindestens 16 Bits (2 Bytes), um diese Informationen zu speichern.

Im Allgemeinen beträgt die Anzahl der möglichen Bereiche für Zahlen von 0 bis n-1 n * (n + 1) / 2. Dies ist weniger als 256, wenn n 22 oder weniger ist: n = 22 ergibt 22 * ​​23/2 = 253 Möglichkeiten. So ein Byte reicht für Teilbereiche von 0-21 .

Ein anderer Weg, um das Problem zu betrachten, ist der folgende: Das Speichern eines Paars von ganzen Zahlen im Bereich von 0 bis n-1 entspricht fast dem Speichern eines Unterbereichs von 0- (n-1) plus einem einzelnen Bit, das bestimmt, ob die erste Zahl vorliegt ist niedriger oder höher als die zweite. (Der Unterschied ergibt sich aus dem Fall, in dem beide Ganzzahlen gleich sind, diese Chance jedoch mit zunehmendem Wert von n immer kleiner wird.) Aus diesem Grund können Sie mit dieser Technik nur ein einziges Bit speichern, und dies ist wahrscheinlich der Hauptgrund, warum sie selten verwendet wird.

Glorfindel
quelle
Vielen Dank. Die Anzahl der für n Bereiche erforderlichen Bits ist log (n) / log2. Wenn ich alles in Wolfram Alpha einspeise, erhalte ich die folgende Excel-kompatible Formel zur Berechnung des Maximalwerts für den Unterbereich für eine bestimmte Anzahl von Bits: = INT ((SQRT (POWER (2, N + 3) + 1) - 1) / 2 )
rghome
9
Die TLDR ist, dass Sie ungefähr ein halbes bisschen gewinnen, so dass es im Allgemeinen nicht wirklich wert ist, komprimiert zu werden.
rghome
Ja, für große N ist es in der Regel ein bisschen zu viel, aber der Aufwand lohnt sich nicht wirklich.
Glorfindel
Zu Ihrer Information, das N + 3 in der Gleichung sieht seltsam aus, aber eine Potenz von 2 ergibt sich aus Ihrer Gleichung und die anderen beiden ergeben sich aus dem 4ac-Teil der quadratischen Formel.
rghome
1
Übrigens, Ihre Zählung reduziert den leeren Bereich, für den alle nicht gezählten Kombinationen stehen. Also n * (n + 1) / 2 + 1! Eine winzige Veränderung.
Deduplizierer
17

Für solch eine kleine Anzahl von Bits ist es unmöglich, viele Bits zu speichern, wie Glorfindel herausgestellt hat . Wenn die von Ihnen verwendete Domain jedoch einige Bits mehr enthält, können Sie im Durchschnitt erhebliche Einsparungen erzielen, indem Sie Bereiche mit dem Startwert und einem Delta codieren.

Nehmen wir an, die Domain sind die ganzen Zahlen, also 32 Bit. Bei der naiven Methode benötigen Sie 64 Bit (Anfang, Ende), um einen Bereich zu speichern.

Wenn wir zu einer Kodierung von (Start, Delta) wechseln, können wir daraus das Ende des Bereichs konstruieren. Wir wissen, dass im schlimmsten Fall der Start 0 ist und das Delta 32 Bits hat.

2 ^ 5 ist 32, also codieren wir die Länge des Deltas in fünf Bits (keine Nulllänge, addieren immer 1) und die Codierung wird (Start, Länge, Delta). Im schlimmsten Fall kostet dies 32 * 2 + 5 Bit, also 69 Bit. Im schlimmsten Fall, wenn alle Bereiche lang sind, ist dies schlechter als die naive Codierung.

Im besten Fall kostet es 32 + 5 + 1 = 38 Bit.

Wenn Sie also viele Bereiche codieren müssen und diese Bereiche jeweils nur einen kleinen Teil Ihrer Domain abdecken, belegen Sie mit dieser Codierung im Durchschnitt weniger Speicherplatz . Es spielt keine Rolle, wie die Starts verteilt sind, da der Start immer 32 Bit dauert, aber es spielt keine Rolle, wie die Längen der Bereiche verteilt sind. Je kleiner die Länge ist, desto besser ist die Komprimierung. Je mehr Bereiche über die gesamte Länge der Domäne verfügbar sind, desto schlechter wird die Codierung.

Wenn Sie jedoch viele Bereiche um ähnliche Startpunkte gruppieren (z. B. weil Sie Werte von einem Sensor erhalten), können Sie noch größere Einsparungen erzielen. Sie können dieselbe Technik auf den Startwert anwenden und eine Abweichung verwenden, um den Startwert zu versetzen.

Nehmen wir an, Sie haben 10000 Bereiche. Die Bereiche sind um einen bestimmten Wert gruppiert. Sie codieren die Vorspannung mit 32 Bits.

Bei Verwendung des naiven Ansatzes würden Sie 32 * 2 * 10 000 = 640 000 Bits benötigen, um alle diese Bereiche zu speichern.

Das Codieren der Vorspannung dauert 32 Bits, und das Codieren jedes Bereichs dauert im besten Fall dann 5 + 1 + 5 + 1 = 12 Bits, was insgesamt 120 000 + 32 = 120 032 Bits ergibt. Im schlimmsten Fall benötigen Sie 5 + 32 + 5 + 32 Bit, also 74 Bit, für insgesamt 740 032 Bit.

Dies bedeutet, dass wir für 10 000 Werte in einer Domäne, für deren Codierung 32 Bit erforderlich sind, Folgendes erhalten

  • 120 032 Bit im besten Fall mit der intelligenten Delta-Codierung
  • 640 000 Bits mit der naiven Start- und Endkodierung, immer (kein bester oder schlechtester Fall)
  • 740 032 Bit mit der Smart-Delta-Codierung im ungünstigsten Fall

Wenn Sie die naive Codierung als Basis nehmen, bedeutet dies entweder Einsparungen von bis zu 81,25% oder bis zu 15,625% mehr Kosten.

Je nachdem, wie Ihre Werte verteilt sind, sind diese Einsparungen erheblich. Kennen Sie Ihre Geschäftsdomäne! Wissen Sie, was Sie codieren möchten.

Als Erweiterung können Sie auch die Vorspannung ändern. Wenn Sie die Daten analysieren und Wertegruppen identifizieren, können Sie die Daten in Gruppen sortieren und jede dieser Gruppen separat mit einer eigenen Verzerrung codieren. Dies bedeutet, dass Sie diese Technik nicht nur auf Bereiche anwenden können, die um einen einzelnen Startwert gruppiert sind, sondern auch auf Bereiche, die um mehrere Werte gruppiert sind.

Wenn Ihre Startpunkte gleichmäßig verteilt sind, funktioniert diese Codierung nicht wirklich gut.

Diese Kodierung ist offensichtlich extrem schlecht zu indizieren. Sie können den x-ten Wert nicht einfach ablesen. Es kann so ziemlich nur sequentiell gelesen werden. Was in manchen Situationen angebracht ist, z. B. Streaming über das Netzwerk oder Massenspeicher (z. B. auf Band oder Festplatte).

Das Auswerten der Daten, das Gruppieren und das Auswählen der richtigen Verzerrung kann ein erheblicher Aufwand sein und erfordert möglicherweise eine Feinabstimmung, um optimale Ergebnisse zu erzielen.

Polygnom
quelle
8

Diese Art von Problem ist Gegenstand von Claude Shannons wegweisendem Aufsatz " Eine mathematische Theorie der Kommunikation" , in dem das Wort "Bit" und mehr oder weniger erfundene Datenkomprimierung eingeführt wurden.

Die allgemeine Idee ist, dass die Anzahl der zum Codieren eines Bereichs verwendeten Bits umgekehrt proportional zur Wahrscheinlichkeit des Auftretens dieses Bereichs ist. Angenommen, der Bereich 45-74 erscheint ungefähr 1/4 der Zeit. Sie können sagen, dass die Sequenz 00 45-74 entspricht. Um den Bereich 45-74 zu codieren, geben Sie "00" aus und halten dort an.

Nehmen wir auch an, dass die Bereiche 99-100 und 140-155 jeweils etwa 1/8 der Zeit erscheinen. Sie können sie jeweils mit einer 3-Bit-Sequenz codieren. Alle 3 Bits reichen aus, solange sie nicht mit „00“ beginnen, das bereits für den Bereich 45-74 reserviert ist.

00: 45-74
010: 99-100
101: 140-155

Sie können auf diese Weise fortfahren, bis jeder mögliche Bereich eine Codierung aufweist. Der am wenigsten wahrscheinliche Bereich benötigt möglicherweise mehr als 100 Bit. Aber das ist okay, weil es selten erscheint.

Es gibt Algorithmen, um die optimale Kodierung zu finden. Ich werde nicht versuchen, sie hier zu erklären, aber Sie können mehr finden, indem Sie den obigen Link besuchen oder nach "Informationstheorie", "Shannon-Fanocodierung" oder "Huffman-Codierung" suchen.

Wie bereits erwähnt, ist es wahrscheinlich besser, die Startnummer und die Differenz zwischen Start- und Endnummer zu speichern. Sie sollten eine Codierung für den Start und eine andere für den Unterschied verwenden, da sie unterschiedliche Wahrscheinlichkeitsverteilungen haben (und ich vermute, dass letztere redundanter ist). Wie von Polygnomen vorgeschlagen, hängt der beste Algorithmus von Ihrer Domain ab.

Patrick McElhaney
quelle
1
Ja, der Geschäftsbereich ist wirklich wichtig. Wir haben tatsächlich erwogen, Huffmann-Codierung für die Verzerrungen für das Startdatum zu verwenden, uns aber schließlich dagegen entschieden, nachdem wir einige statistische Analysen an realen Daten durchgeführt hatten. Die einfache Verwendung der gleichen Codierung für Bias und Delta war wichtiger als das Hinzufügen von Huffmann, und Sie müssen auch den gesamten Huffmann-Baum senden. Es ist jedoch eine gute Idee, die Huffmann-Codierung im Auge zu behalten.
Polygnome
1

So erweitern Sie die Antwort von @Glorfindel:

Wie n → ∞, (n - 1) → n. Also Ω (Bereiche) → n² / 2 und log (Ω (Bereiche)) → (2n - 1). Da die naive Codierung 2n Bit benötigt, spart die asymptotische maximale Komprimierung nur 1 Bit.

Jared Goguen
quelle
1

Es gibt eine ähnliche Antwort, aber um eine optimale Komprimierung zu erzielen, benötigen Sie:

  1. Eine optimale Entropiecodierungsmethode (Lesen der arithmetischen Codierung und der im Wesentlichen äquivalenten Methode ( gleiches Komprimierungsverhältnis, etwas schneller, aber auch schwieriger zu erfassen) ANS )
  2. So viele Informationen wie möglich über die Verteilung der Daten. Entscheidend ist dabei nicht nur, dass man "errät", wie oft eine Zahl vorkommt, sondern dass man bestimmte Möglichkeiten mit Sicherheit ausschließen kann. Beispielsweise können Sie Intervalle negativer Größe und möglicherweise der Größe 0 ausschließen, je nachdem, wie Sie ein gültiges Intervall definieren. Wenn Sie mehrere Intervalle gleichzeitig codieren müssen, können Sie diese sortieren, z. B. nach abnehmender Breite oder zunehmendem Start- / Endwert, und eine ganze Reihe von Werten ausschließen (z. B. wenn Sie eine Reihenfolge durch Verringern der Breite garantieren, das vorherige Intervall) hatte eine Breite von 100 und der Startwert für den nächsten ist 47, müssen Sie nur die Möglichkeiten bis 147 für Endwerte berücksichtigen).

Wichtig ist, dass Nummer 2 bedeutet, dass Sie die Dinge so codieren möchten, dass die informativsten Werte (pro codiertem Bit) an erster Stelle stehen. Während ich beispielsweise vorschlug, eine sortierte Liste "wie sie ist" zu codieren, wäre es normalerweise klüger, sie als "binären Baum" zu lencodieren len/2. Angenommen, es hatte die Breite w. Jetzt kennen Sie alle Elemente, bevor sie irgendwo in [0, w] eine Breite haben, und alle Elemente, nachdem sie irgendwo in [w, max val you accept] eine Breite haben. Wiederholen Sie diesen Vorgang rekursiv (Unterteilen Sie jede halbe Liste erneut in zwei Hälften usw.), bis Sie die lenElemente abgedeckt haben (sofern nichts anderes festgelegt ist, möchten Sie sie codierenlenzuerst, damit Sie sich nicht mit dem Beenden von Tokens herumschlagen müssen). Wenn "max val you accept" wirklich offen ist, kann es sinnvoll sein, zuerst den höchsten Wert zu codieren, der tatsächlich in Ihren Daten erscheint, dh das letzte Element, und dann die binäre Partitionierung durchzuführen. Wiederum ist das, was am informativsten ist, pro Bit zuerst.

Wenn Sie zuerst die Breite des Intervalls codieren und den maximal möglichen Wert kennen, mit dem Sie es zu tun haben, können Sie natürlich alle Startwerte ausschließen, die zu einem Überlauf führen würden ... Sie haben die Idee. Transformieren und ordnen Sie Ihre Daten so, dass Sie so viel wie möglich auf den Rest der Daten schließen können, während Sie sie dekodieren. Ein optimaler Entropie-Kodierungsalgorithmus stellt sicher, dass Sie keine Bits für Kodierungsinformationen verschwenden, die Sie "bereits kennen". .

tohoho
quelle