Meine Lösung: bester Fall 7,025 Bit / Nummer, schlechtester Fall 14,193 Bit / Nummer, grober Durchschnitt 8,551 Bit / Nummer. Stream-codiert, kein wahlfreier Zugriff.
Noch bevor ich die Antwort von ruslik gelesen habe, habe ich sofort daran gedacht, den Unterschied zwischen den einzelnen Zahlen zu kodieren, da er klein ist und relativ konsistent sein sollte, aber die Lösung muss auch das Worst-Case-Szenario berücksichtigen können. Wir haben ein Leerzeichen von 100000 Zahlen, die nur 1000 Zahlen enthalten. In einem vollkommen einheitlichen Telefonbuch wäre jede Nummer um 100 größer als die vorherige Nummer:
55555-12 3 45
55555-12 4 45
55555-12 5 45
In diesem Fall wäre kein Speicher erforderlich, um die Unterschiede zwischen den Zahlen zu codieren, da es sich um eine bekannte Konstante handelt. Leider können die Zahlen von den idealen Schritten von 100 abweichen. Ich würde die Differenz zum idealen Inkrement von 100 codieren, so dass ich, wenn sich zwei benachbarte Zahlen um 103 unterscheiden, die Zahl 3 codieren würde und wenn sich zwei benachbarte Zahlen um 92 unterscheiden, I. würde -8 codieren. Ich nenne das Delta aus einem idealen Inkrement von 100 die " Varianz ".
Die Varianz kann von -99 (dh zwei aufeinanderfolgende Nummern) bis 99000 reichen (das gesamte Telefonbuch besteht aus den Nummern 00000… 00999 und einer zusätzlichen am weitesten entfernten Nummer 99999), was einem Bereich von 99100 möglichen Werten entspricht.
Ich würde versuchen , einen minimalen Speicher zuzuweisen, um die häufigsten Unterschiede zu codieren und den Speicher zu erweitern, wenn ich auf größere Unterschiede stoße (wie bei ProtoBufvarint
). Ich verwende Blöcke mit sieben Bits, sechs für die Speicherung und ein zusätzliches Flag-Bit am Ende, um anzuzeigen, dass diese Varianz mit einem zusätzlichen Block nach dem aktuellen gespeichert wird, bis zu maximal drei Blöcken (was ein Maximum von ergibt 3 * 6 = 18 Speicherbits, die 262144 mögliche Werte sind, mehr als die Anzahl der möglichen Varianzen (99100). Jeder zusätzliche Block, der einem erhöhten Flag folgt, hat Bits von höherer Bedeutung, sodass der erste Block immer die Bits 0- hat. In 5 hat der optionale zweite Block die Bits 6-11 und der optionale dritte Block hat die Bits 12-17.
Ein einzelner Block bietet sechs Speicherbits, die 64 Werte aufnehmen können. Ich möchte die 64 kleinsten Abweichungen so zuordnen, dass sie in diesen einzelnen Block passen (dh Abweichungen von -32 bis +31), daher verwende ich die ProtoBuf-ZigZag-Codierung bis zu den Abweichungen von -99 bis +98 (da keine Notwendigkeit besteht) für eine negative Varianz über -99), an diesem Punkt wechsle ich zur regulären Codierung, versetzt um 98:
Varianz | Codierter Wert
----------- + ----------------
0 | 0
-1 | 1
1 | 2
-2 | 3
2 | 4
-3 | 5
3 | 6
... | ...
-31 | 61
31 | 62
-32 | 63
----------- | --------------- 6 Bit
32 | 64
-33 | 65
33 | 66
... | ...
-98 | 195
98 | 196
-99 | 197
----------- | --------------- Ende von ZigZag
100 | 198
101 | 199
... | ...
3996 | 4094
3997 | 4095
----------- | --------------- 12 Bit
3998 | 4096
3999 | 4097
... | ...
262045 | 262143
----------- | --------------- 18 Bit
Einige Beispiele dafür, wie Abweichungen als Bits codiert werden, einschließlich des Flags, um einen zusätzlichen Block anzuzeigen:
Varianz | Codierte Bits
----------- + ----------------
0 | 000000 0
5 | 001010 0
-8 | 001111 0
-32 | 111111 0
32 | 000000 1 000001 0
-99 | 000101 1 000011 0
177 | 010011 1 000100 0
14444 | 001110 1 100011 1 000011 0
Die ersten drei Nummern eines Beispieltelefonbuchs würden also wie folgt als Bitstrom codiert:
BIN 000101001011001000100110010000011001 000110 1 010110 1 00001 0
PH # 55555-12345 55555-12448 55555-12491
POS 1 2 3
Im besten Fall ist das Telefonbuch etwas gleichmäßig verteilt und es gibt keine zwei Telefonnummern mit einer Varianz von mehr als 32, sodass 7 Bit pro Nummer plus 32 Bit für die Startnummer für insgesamt 32 + 7 * 999 verwendet werden = 7025 Bit .
Ein gemischtes Szenario , bei dem die Varianz von 800 Telefonnummern in einen Block passt (800 * 7 = 5600), 180 Nummern in jeweils zwei Blöcke passen (180 * 2 * 7 = 2520) und 19 Nummern in jeweils drei Blöcke passen (20 * 3) * 7 = 399) plus die anfänglichen 32 Bits ergeben 8551 Bits .
Im schlimmsten Fall passen 25 Zahlen in drei Blöcke (25 * 3 * 7 = 525 Bit) und die verbleibenden 974 Zahlen passen in zwei Blöcke (974 * 2 * 7 = 13636 Bit) plus 32 Bit für die erste Zahl für einen Flügel insgesamt14193 Bits .
Anzahl der verschlüsselten Nummern |
1 Stück | 2 Stücke | 3 Stücke | Gesamtbits
--------- + ---------- + ---------- + ------------
999 | 0 | 0 | 7025
800 | 180 | 19 | 8551
0 | 974 | 25 | 14193
Ich sehe vier zusätzliche Optimierungen, die durchgeführt werden können, um den erforderlichen Speicherplatz weiter zu reduzieren:
- Der dritte Block benötigt nicht die vollen sieben Bits, er kann nur fünf Bits und ohne Flag-Bit sein.
- Es kann eine erste Übergabe der Zahlen geben, um die besten Größen für jeden Block zu berechnen. Vielleicht wäre es für ein bestimmtes Telefonbuch optimal, wenn der erste Block 5 + 1 Bits, der zweite 7 + 1 und der dritte 5 + 1 hat. Dies würde die Größe weiter auf ein Minimum von 6 * 999 + 32 = 6026 Bit plus zwei Sätze von drei Bits reduzieren, um die Größen der Blöcke 1 und 2 (die Größe von Block 3 ist der Rest der erforderlichen 16 Bits) für insgesamt zu speichern von 6032 Bits!
- Der gleiche anfängliche Durchgang kann ein besseres erwartetes Inkrement als das Standard-100 berechnen. Möglicherweise gibt es ein Telefonbuch, das von 55555-50000 beginnt und daher den halben Nummernbereich hat, sodass das erwartete Inkrement 50 betragen sollte. Oder es gibt ein nichtlineares Verteilung (Standardabweichung vielleicht) und ein anderes optimales erwartetes Inkrement können verwendet werden. Dies würde die typische Varianz verringern und möglicherweise die Verwendung eines noch kleineren ersten Blocks ermöglichen.
- Weitere Analysen können im ersten Durchgang durchgeführt werden, damit das Telefonbuch partitioniert werden kann, wobei jede Partition ihre eigenen erwarteten Inkrement- und Blockgrößenoptimierungen aufweist. Dies würde eine kleinere erste Blockgröße für bestimmte sehr einheitliche Teile des Telefonbuchs (Verringerung der Anzahl der verbrauchten Bits) und größere Blockgrößen für ungleichmäßige Teile (Verringerung der Anzahl der für Fortsetzungsflags verschwendeten Bits) ermöglichen.