Dies ist eine Frage zum Google-Interview:
Es müssen ungefähr tausend Telefonnummern mit jeweils 10 Ziffern gespeichert werden. Sie können davon ausgehen, dass die ersten 5 Ziffern über tausend Zahlen hinweg gleich sind. Sie müssen die folgenden Vorgänge ausführen: a. Suchen Sie, ob eine bestimmte Nummer vorhanden ist. b. Drucken Sie die ganze Nummer
Was ist der effizienteste platzsparende Weg, um dies zu tun?
Ich antwortete auf die Hash-Tabelle und später auf die Huffman-Codierung, aber mein Interviewer sagte, ich gehe nicht in die richtige Richtung. Bitte helfen Sie mir hier.
Könnte die Verwendung eines Suffixes helfen?
Idealerweise benötigt das Speichern von 1000 Nummern 4 Bytes pro Nummer, sodass insgesamt 4000 Bytes zum Speichern von 1000 Nummern benötigt werden. Quantitativ möchte ich den Speicher auf <4000 Bytes reduzieren, erklärte mir mein Interviewer.
quelle
Antworten:
Hier ist eine Verbesserung der Antwort von aix . Erwägen Sie die Verwendung von drei "Schichten" für die Datenstruktur: Die erste ist eine Konstante für die ersten fünf Ziffern (17 Bit); Von nun an hat jede Telefonnummer nur noch die verbleibenden fünf Ziffern. Wir betrachten diese verbleibenden fünf Ziffern als 17-Bit-Binärzahlen und speichern k dieser Bits mit einer Methode und 17 - k = m mit einer anderen Methode, wobei wir am Ende k bestimmen, um den erforderlichen Speicherplatz zu minimieren.
Wir sortieren zuerst die Telefonnummern (alle auf 5 Dezimalstellen reduziert). Dann zählen wir, wie viele Telefonnummern es gibt, für die die aus den ersten m Bits bestehende Binärzahl alle 0 ist, für wie viele Telefonnummern die ersten m Bits höchstens 0 ... 01 sind, für wie viele Telefonnummern die ersten m Bits sind höchstens 0 ... 10 usw. bis zur Anzahl der Telefonnummern, für die die ersten m Bits 1 ... 11 sind - diese letzte Anzahl ist 1000 (dezimal). Es gibt 2 ^ m solcher Zählungen und jede Zählung ist höchstens 1000. Wenn wir die letzte weglassen (weil wir wissen, dass es sowieso 1000 ist), können wir alle diese Zahlen in einem zusammenhängenden Block von (2 ^ m - 1) speichern. * 10 Bits. (10 Bit reichen aus, um eine Zahl unter 1024 zu speichern.)
Die letzten k Bits aller (reduzierten) Telefonnummern werden zusammenhängend im Speicher gespeichert; Wenn also k beispielsweise 7 ist, entsprechen die ersten 7 Bits dieses Speicherblocks (Bits 0 bis 6) den letzten 7 Bits der ersten (reduzierten) Telefonnummer, die Bits 7 bis 13 entsprechen den letzten 7 Bits der zweiten (reduzierten) Telefonnummer usw. Dies erfordert 1000 * k Bits für insgesamt 17 + (2 ^ (17 - k ) - 1) * 10 + 1000 * k , was sein Minimum von 11287 für k = 10 erreicht. Wir können also alle Telefonnummern in Ceil speichern ( 11287/8) = 1411 Bytes.
Zusätzlicher Platz kann gespart werden, indem beobachtet wird, dass keine unserer Zahlen mit z. B. 1111111 (binär) beginnen kann, da die niedrigste Zahl, die damit beginnt, 130048 ist und wir nur fünf Dezimalstellen haben. Dies ermöglicht es uns, einige Einträge aus dem ersten Speicherblock zu entfernen : Anstelle von 2 ^ m - 1 Zählungen benötigen wir nur Ceil (99999/2 ^ k ). Das heißt, die Formel wird
17 + Ceil (99999/2 ^ k ) * 10 + 1000 * k
was erstaunlicherweise sein Minimum von 10997 sowohl für k = 9 als auch für k = 10 oder Ceil (10997/8) = 1375 Bytes erreicht.
Wenn wir wissen möchten, ob eine bestimmte Telefonnummer in unserem Set enthalten ist, prüfen wir zunächst, ob die ersten fünf Binärziffern mit den fünf von uns gespeicherten Ziffern übereinstimmen. Dann teilen wir die verbleibenden fünf Ziffern in die oberen m = 7 Bits (dh die m- Bit-Zahl M ) und die unteren k = 10 Bits (die Zahl K ) auf. Wir finden nun die Nummer a [M-1] reduzierter Telefonnummern, für die die ersten m Ziffern höchstens M - 1 sind, und die Nummer a [M] reduzierter Telefonnummern, für die die ersten m Ziffern höchstens M sind. beide aus dem ersten Bitblock. Wir prüfen nun zwischen dem a[M-1] -te und eine [M] -te Folge von k Bits im zweiten Speicherblock, um zu sehen, ob wir K finden ; Im schlimmsten Fall gibt es 1000 solcher Sequenzen. Wenn wir also die binäre Suche verwenden, können wir mit O-Operationen (log 1000) fertig werden.
Pseudo - Code für alle 1000 Nummern Druck folgt, wobei I die Zugriff K 'th k -Bit - Eintrag des ersten Speicherblock als ein [K] und der M -te m -Bit - Eintrag des zweiten Blocks des Speichers als b [M] (Beides würde einige Bitoperationen erfordern, deren Ausschreiben mühsam ist). Die ersten fünf Ziffern sind in der Nummer c .
Vielleicht stimmt etwas mit dem Grenzfall für K = Ceil (99999/2 ^ k ) nicht, aber das ist leicht zu beheben.
Schließlich ist es aus entropischer Sicht nicht möglich, eine Teilmenge von 10 ^ 3 positiven ganzen Zahlen, die alle kleiner als 10 ^ 5 sind, in weniger als Ceil zu speichern (log [2] (Binomial (10 ^ 5, 10 ^ 3)). ) = 8073. Einschließlich der 17, die wir für die ersten 5 Ziffern benötigen, gibt es immer noch eine Lücke von 10997 - 8090 = 2907 Bits. Es ist eine interessante Herausforderung zu sehen, ob es bessere Lösungen gibt, bei denen Sie noch relativ effizient auf die Nummern zugreifen können!
quelle
Im Folgenden behandle ich die Zahlen als ganzzahlige Variablen (im Gegensatz zu Zeichenfolgen):
Um es noch einmal zusammenzufassen: Die ersten 17 Bits sind das gemeinsame Präfix, die nachfolgenden 1000 Gruppen von 17 Bits sind die letzten fünf Ziffern jeder Nummer, die in aufsteigender Reihenfolge gespeichert sind.
Insgesamt betrachten wir 2128 Bytes für die 1000 Nummern oder 17.017 Bits pro 10-stelliger Telefonnummer.
Suche ist
O(log n)
(binäre Suche) und vollständige Aufzählung istO(n)
.quelle
k
ist also irrelevant.http://en.wikipedia.org/wiki/Acyclic_deterministic_finite_automaton
Ich hatte einmal ein Interview, in dem sie nach Datenstrukturen fragten. Ich habe "Array" vergessen.
quelle
Ich würde wahrscheinlich in Betracht ziehen, eine komprimierte Version eines Trie zu verwenden (möglicherweise eine DAWG, wie von @Misha vorgeschlagen).
Das würde automatisch die Tatsache ausnutzen, dass sie alle ein gemeinsames Präfix haben.
Die Suche wird in konstanter Zeit durchgeführt, und das Drucken wird in linearer Zeit durchgeführt.
quelle
Ich habe schon einmal von diesem Problem gehört (aber ohne die Annahme, dass die ersten 5 Ziffern gleich sind), und der einfachste Weg, dies zu tun, war Rice Coding :
1) Da die Reihenfolge keine Rolle spielt, können wir sie sortieren und nur Unterschiede zwischen aufeinanderfolgenden Werten speichern. In unserem Fall wären die durchschnittlichen Unterschiede 100.000 / 1000 = 100
2) Codieren Sie die Unterschiede mit Reiscodes (Basis 128 oder 64) oder sogar Golomb-Codes (Basis 100).
EDIT: Eine Schätzung für die Rice-Codierung mit Basis 128 (nicht weil dies die besten Ergebnisse liefern würde, sondern weil es einfacher zu berechnen ist):
Wir speichern den ersten Wert wie er ist (32 Bit).
Die restlichen 999 Werte sind Unterschiede (wir erwarten, dass sie klein sind, durchschnittlich 100) und enthalten:
unärer Wert
value / 128
(variable Anzahl von Bits + 1 Bit als Abschlusszeichen)Binärwert für
value % 128
(7 Bits)Wir müssen die Grenzen (nennen wir es
VBL
) für die Anzahl der variablen Bits irgendwie schätzen :untere Grenze: Bedenken Sie, dass wir Glück haben und kein Unterschied größer ist als unsere Basis (in diesem Fall 128). Dies würde bedeuten, 0 zusätzliche Bits zu geben.
Obergrenze: Da alle Unterschiede, die kleiner als die Basis sind, im binären Teil der Zahl codiert werden, müssten wir maximal 100000/128 = 781,25 codieren (noch weniger, da wir nicht erwarten, dass die meisten Unterschiede Null sind ).
Das Ergebnis ist also 32 + 999 * (1 + 7) + variable (0..782) Bits = 1003 + variable (0..98) Bytes.
quelle
32 + 999 * (1 + 7 + variable(0..782))
Bits? Jede der 999 Nummern benötigt eine Darstellung vonvalue / 128
.Dies ist ein bekanntes Problem aus Bentleys Programmierperlen.
Lösung: Entfernen Sie die ersten fünf Ziffern von den Zahlen, da sie für jede Zahl gleich sind. Verwenden Sie dann bitweise Operationen, um den verbleibenden 9999 möglichen Wert darzustellen. Sie benötigen nur 2 ^ 17 Bits, um die Zahlen darzustellen. Jedes Bit repräsentiert eine Zahl. Wenn das Bit gesetzt ist, befindet sich die Nummer im Telefonbuch.
Um alle Zahlen zu drucken, drucken Sie einfach alle Zahlen, bei denen das Bit gesetzt ist, verkettet mit dem Präfix. Um nach einer bestimmten Zahl zu suchen, führen Sie die erforderliche Bitarithmetik durch, um die bitweise Darstellung der Zahl zu überprüfen.
Sie können in O (1) nach einer Zahl suchen, und die Raumeffizienz ist aufgrund der Bitrepräsentation maximal.
HTH Chris.
quelle
Feste Speicherung von 1073 Bytes für 1.000 Nummern:
Das Grundformat dieser Speichermethode besteht darin, die ersten 5 Ziffern, eine Anzahl für jede Gruppe und den Versatz für jede Zahl in jeder Gruppe zu speichern.
Präfix:
Unser 5-stelliges Präfix nimmt die ersten 17 Bits ein .
Gruppierung:
Als nächstes müssen wir eine Gruppierung mit guter Größe für Zahlen herausfinden. Versuchen wir, ungefähr 1 Nummer pro Gruppe zu haben. Da wir wissen, dass ungefähr 1000 Nummern gespeichert werden müssen, teilen wir 99.999 in ungefähr 1000 Teile auf. Wenn wir die Gruppengröße als 100 wählen würden, würden Bits verschwendet. Versuchen wir also eine Gruppengröße von 128, die mit 7 Bits dargestellt werden kann. Dies gibt uns 782 Gruppen, mit denen wir arbeiten können.
Anzahl : Als nächstes müssen wir für jede der 782 Gruppen die Anzahl der Einträge in jeder Gruppe speichern. Eine 7-Bit-Zählung für jede Gruppe würde ergeben
7*782=5,474 bits
, was sehr ineffizient ist, da die durchschnittliche dargestellte Anzahl aufgrund der Auswahl unserer Gruppen etwa 1 beträgt.Daher haben wir stattdessen Zählungen variabler Größe mit führenden Einsen für jede Zahl in einer Gruppe, gefolgt von einer 0. Wenn wir also
x
Zahlen in einer Gruppe hätten, hätten wirx 1's
eine0
Zählung gefolgt , um die Zählung darzustellen. Wenn wir zum Beispiel 5 Zahlen in einer Gruppe hätten, würde die Anzahl durch dargestellt111110
. Wenn bei dieser Methode 1.000 Zahlen vorhanden sind, erhalten wir 1000 Einsen und 782 Nullen für insgesamt 1000 + 782 = 1.782 Bits für die Zählungen .Versatz:
Zuletzt ist das Format jeder Zahl nur der 7-Bit-Versatz für jede Gruppe. Wenn beispielsweise 00000 und 00001 die einzigen Zahlen in der Gruppe 0-127 sind, wären die Bits für diese Gruppe
110 0000000 0000001
. Unter der Annahme von 1.000 Zahlen gibt es 7.000 Bits für die Offsets .Daher lautet unsere endgültige Zählung unter der Annahme von 1.000 Zahlen wie folgt:
Lassen Sie uns nun überprüfen, ob unsere Auswahl der Gruppengröße durch Aufrunden auf 128 Bit die beste Wahl für die Gruppengröße war. Wenn Sie
x
die Anzahl der Bits für jede Gruppe auswählen, lautet die Formel für die Größe:Das Minimieren dieser Gleichung für ganzzahlige Werte von
x
ergibtx=6
, was 8.580 Bits = 1.073 Bytes ergibt . Daher ist unser idealer Speicher wie folgt:Gesamtspeicher:
1017 (prefix plus 1's) + 1563 (0's in count) + 6*1000 (offsets) = 8,580 bits = 1,073 bytes
quelle
Wenn man dies als rein theoretisches Problem betrachtet und die Implementierung unter Kontrolle lässt, besteht der effizienteste Weg darin, alle möglichen Sätze von 10000 letzten Ziffern in einer riesigen Indizierungstabelle zu indizieren. Angenommen, Sie haben genau 1000 Zahlen, würden Sie etwas mehr als 8000 Bits benötigen, um den aktuellen Satz eindeutig zu identifizieren. Es ist keine größere Komprimierung möglich, da Sie dann zwei Sätze haben würden, die mit demselben Status identifiziert werden.
Das Problem dabei ist, dass Sie jeden der 2 ^ 8000 Sätze in Ihrem Programm als Lut darstellen müssten und nicht einmal Google dazu in der Lage wäre.
Die Suche wäre O (1) und druckt alle Zahlen O (n). Die Einfügung wäre O (2 ^ 8000), was theoretisch O (1) ist, in der Praxis jedoch unbrauchbar ist.
In einem Interview würde ich diese Antwort nur geben, wenn ich sicher wäre, dass das Unternehmen jemanden sucht, der in der Lage ist, viel über den Tellerrand hinauszudenken. Andernfalls könnten Sie wie ein Theoretiker aussehen, der keine Bedenken in der realen Welt hat.
EDIT : Ok, hier ist eine "Implementierung".
Schritte zum Erstellen der Implementierung:
Dies ist nicht das Programm, sondern eine Art Metaprogramm, das eine gigantische LUT erstellt, die jetzt in einem Programm verwendet werden kann. Konstante Programmdaten werden bei der Berechnung der Raumeffizienz normalerweise nicht berücksichtigt, daher ist uns dieses Array bei unseren endgültigen Berechnungen egal.
So verwenden Sie diese LUT:
Dies bedeutet, dass wir für die Speicherung nur 8091 Bit benötigen, was sich hier als optimale Codierung erwiesen hat. Das Finden des richtigen Blocks erfordert jedoch O (100 000 * (100 000 wählen 1000)), was nach mathematischen Regeln O (1) ist, in der Praxis jedoch immer länger dauert als die Zeit des Universums.
Die Suche ist jedoch einfach:
Das Drucken aller Zahlen ist ebenfalls einfach (und nimmt tatsächlich O (100000) = O (1) an, da Sie immer alle Bits des aktuellen Blocks überprüfen müssen, also habe ich dies oben falsch berechnet).
Ich würde dies nicht als "Implementierung" bezeichnen, da die Einschränkungen (Größe des Universums und Zeit, in der dieses Universum gelebt hat oder diese Erde existieren wird) offensichtlich missachtet werden. Theoretisch ist dies jedoch die optimale Lösung. Bei kleineren Problemen kann dies tatsächlich durchgeführt werden und wird manchmal auch durchgeführt. Zum Beispiel Sortiernetze sind ein Beispiel für diese Art der Codierung und können als letzten Schritt in rekursiven Sortieralgorithmen verwendet werden, um eine möglichst hohe Geschwindigkeit zu bekommen.
quelle
Dies entspricht dem Speichern von tausend nicht negativen Ganzzahlen mit jeweils weniger als 100.000. Wir können dazu so etwas wie eine arithmetische Codierung verwenden.
Letztendlich werden die Nummern in einer sortierten Liste gespeichert. Ich stelle fest, dass der erwartete Unterschied zwischen benachbarten Zahlen in der Liste 100.000 / 1000 = 100 beträgt, was in 7 Bits dargestellt werden kann. Es wird auch viele Fälle geben, in denen mehr als 7 Bits erforderlich sind. Eine einfache Möglichkeit, diese weniger häufigen Fälle darzustellen, besteht darin, das utf-8-Schema zu verwenden, bei dem ein Byte eine 7-Bit-Ganzzahl darstellt, sofern nicht das erste Bit gesetzt ist. In diesem Fall wird das nächste Byte gelesen, um eine 14-Bit-Ganzzahl zu erzeugen, es sei denn Das erste Bit wird gesetzt. In diesem Fall wird das nächste Byte gelesen, um eine 21-Bit-Ganzzahl darzustellen.
So kann mindestens die Hälfte der Unterschiede zwischen aufeinanderfolgenden ganzen Zahlen mit einem Byte dargestellt werden, und fast alle anderen erfordern zwei Bytes. Einige Zahlen, die durch größere Unterschiede als 16.384 getrennt sind, benötigen drei Bytes, von denen jedoch nicht mehr als 61 vorhanden sein können. Der durchschnittliche Speicher beträgt dann ungefähr 12 Bit pro Nummer oder etwas weniger oder höchstens 1500 Bytes.
Der Nachteil dieses Ansatzes ist, dass die Überprüfung der Existenz einer Zahl jetzt O (n) ist. Es wurde jedoch keine zeitliche Komplexitätsanforderung angegeben.
Nach dem Schreiben bemerkte ich, dass Ruslik bereits die oben genannte Differenzmethode vorgeschlagen hatte. Der einzige Unterschied ist das Codierungsschema. Meins ist wahrscheinlich einfacher, aber weniger effizient.
quelle
Nur um schnell nach einem Grund zu fragen, warum wir die Zahlen nicht in eine Basis 36 ändern möchten. Es spart vielleicht nicht so viel Platz, aber es würde sicher Zeit bei der Suche sparen, da Sie viel weniger als 10 Punkte betrachten werden. Oder ich würde sie je nach Gruppe in Dateien aufteilen. Also würde ich eine Datei (111) -222.txt benennen und dann nur Zahlen speichern, die in diese Gruppe passen, und sie dann in numerischer Reihenfolge durchsuchbar machen. Auf diese Weise kann ich immer chacken, um zu sehen, ob die Datei beendet wird. bevor ich eine größere Suche durchführe. oder um richtig zu sein, würde ich eine binäre Suche nach der Datei durchführen, um zu sehen, ob sie beendet wird. und eine weitere Bonary-Suche nach dem Inhalt der Datei
quelle
Warum nicht einfach halten? Verwenden Sie ein Array von Strukturen.
Wir können also die ersten 5 Ziffern als Konstante speichern, vergessen Sie diese also vorerst.
65535 kann höchstens in einer 16-Bit-Nummer gespeichert werden, und die maximale Anzahl, die wir haben können, ist 99999, was mit der 17. Bit-Nummer mit einem Maximum von 131071 übereinstimmt.
Die Verwendung von 32-Bit-Datentypen ist eine Verschwendung, da wir nur 1 Bit dieser zusätzlichen 16-Bit-Daten benötigen. Daher können wir eine Struktur definieren, die einen Booleschen Wert (oder ein Zeichen) und eine 16-Bit-Zahl enthält.
Angenommen, C / C ++
Diese Struktur nimmt nur 3 Bytes ein, und wir benötigen ein Array von 1000, also insgesamt 3000 Bytes. Wir haben den Gesamtraum um 25% reduziert!
Was das Speichern der Zahlen angeht, können wir einfach bitweise rechnen
Und das Gegenteil
Um alle zu drucken, können wir eine einfache Schleife über das Array verwenden. Das Abrufen einer bestimmten Nummer erfolgt natürlich in konstanter Zeit, da es sich um ein Array handelt.
Um nach einer Nummer zu suchen, benötigen wir ein sortiertes Array. Wenn die Zahlen gespeichert sind, sortieren Sie das Array (ich würde persönlich eine Zusammenführungssortierung wählen, O (nlogn)). Um jetzt zu suchen, würde ich einen Zusammenführungssortierungsansatz wählen. Teilen Sie das Array auf und sehen Sie, zwischen welchem unsere Nummer liegt. Rufen Sie dann die Funktion nur für dieses Array auf. Führen Sie dies rekursiv durch, bis Sie eine Übereinstimmung haben und den Index zurückgeben. Andernfalls ist er nicht vorhanden und drucken Sie einen Fehlercode. Diese Suche wäre ziemlich schnell, und der schlimmste Fall ist immer noch besser als O (nlogn), da er absolut in kürzerer Zeit als die Zusammenführungssortierung ausgeführt wird (jedes Mal wird nur 1 Seite des Splits rekursiert, anstatt beide Seiten :)) ist O (nlogn).
quelle
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 ProtoBuf
varint
). 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:
Einige Beispiele dafür, wie Abweichungen als Bits codiert werden, einschließlich des Flags, um einen zusätzlichen Block anzuzeigen:
Die ersten drei Nummern eines Beispieltelefonbuchs würden also wie folgt als Bitstrom codiert:
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 .
Ich sehe vier zusätzliche Optimierungen, die durchgeführt werden können, um den erforderlichen Speicherplatz weiter zu reduzieren:
quelle
Die eigentliche Frage ist die Speicherung von fünfstelligen Telefonnummern.
Der Trick ist, dass Sie 17 Bit benötigen, um den Zahlenbereich von 0..99.999 zu speichern. Das Speichern von 17 Bit an herkömmlichen 8-Byte-Wortgrenzen ist jedoch ein Aufwand. Aus diesem Grund fragen sie, ob Sie in weniger als 4 KB arbeiten können, wenn Sie keine 32-Bit-Ganzzahlen verwenden.
Frage: Sind alle Zahlenkombinationen möglich?
Aufgrund der Art des Telefonsystems sind möglicherweise weniger als 65.000 Kombinationen möglich. Ich gehe davon aus, dass ja, weil wir über die letzten fünf Stellen in der Telefonnummer sprechen, im Gegensatz zu der Vorwahl oder den Austauschpräfixen.
Frage: Wird diese Liste statisch sein oder müssen Updates unterstützt werden?
Wenn es statisch ist, zählen Sie beim Auffüllen der Datenbank die Anzahl der Stellen <50.000 und die Anzahl der Stellen> = 50.000. Ordnen Sie zwei Arrays mit
uint16
geeigneter Länge zu: eines für die ganzen Zahlen unter 50.000 und eines für die höhere Menge. Wenn Sie Ganzzahlen im höheren Array speichern, subtrahieren Sie 50.000 und addieren Sie 50.000, wenn Sie Ganzzahlen aus diesem Array lesen. Jetzt haben Sie Ihre 1.000 Ganzzahlen in 2.000 8-Byte-Wörtern gespeichert.Das Erstellen des Telefonbuchs erfordert zwei Eingabeverläufe, aber das Nachschlagen sollte im Durchschnitt in der Hälfte der Zeit erfolgen, als dies bei einem einzelnen Array der Fall wäre. Wenn die Suchzeit sehr wichtig wäre, könnten Sie mehr Arrays für kleinere Bereiche verwenden, aber ich denke, bei diesen Größen würde Ihre Leistungsgrenze die Arrays aus dem Speicher ziehen und 2k wird wahrscheinlich im CPU-Cache gespeichert, wenn Sie keinen Speicherplatz für irgendetwas registrieren, das Sie diese verwenden würden Tage.
Wenn es dynamisch ist , weisen Sie ein Array von etwa 1000 zu
uint16
und fügen Sie die Zahlen in sortierter Reihenfolge hinzu. Setzen Sie das erste Byte auf 50.001 und das zweite Byte auf einen geeigneten Nullwert wie NULL oder 65.000. Wenn Sie die Nummern speichern, speichern Sie sie in sortierter Reihenfolge. Wenn eine Zahl unter 50.001 liegt, speichern Sie sie vor dem 50.001-Marker. Wenn eine Zahl 50.001 oder höher ist, speichern Sie sie nach dem 50.001-Marker, subtrahieren Sie jedoch 50.000 vom gespeicherten Wert.Ihr Array sieht ungefähr so aus:
Wenn Sie also eine Nummer im Telefonbuch nachschlagen, durchlaufen Sie das Array. Wenn Sie den Wert 50.001 erreicht haben, fügen Sie Ihren Array-Werten 50.000 hinzu.
Dies macht Beilagen sehr teuer, aber das Nachschlagen ist einfach und Sie werden nicht viel mehr als 2k für die Speicherung ausgeben.
quelle