Ich habe diese Interviewfrage erhalten:
Geben Sie bei einer Eingabedatei mit vier Milliarden Ganzzahlen einen Algorithmus zum Generieren einer Ganzzahl an, die nicht in der Datei enthalten ist. Angenommen, Sie haben 1 GB Speicher. Folgen Sie Ihren Anweisungen, wenn Sie nur 10 MB Arbeitsspeicher haben.
Meine Analyse:
Die Größe der Datei beträgt 4 × 10 9 × 4 Bytes = 16 GB.
Wir können extern sortieren und so den Bereich der ganzen Zahlen kennen.
Meine Frage ist, wie man die fehlende Ganzzahl in den sortierten großen Ganzzahlensätzen am besten erkennt.
Mein Verständnis (nachdem ich alle Antworten gelesen habe):
Angenommen, es handelt sich um 32-Bit-Ganzzahlen, dann gibt es 2 32 = 4 * 10 9 verschiedene Ganzzahlen.
Fall 1: Wir haben 1 GB = 1 * 10 9 * 8 Bit = 8 Milliarden Bit Speicher.
Lösung:
Wenn wir ein Bit verwenden, das eine bestimmte Ganzzahl darstellt, reicht dies aus. Wir brauchen keine Sortierung.
Implementierung:
int radix = 8;
byte[] bitfield = new byte[0xffffffff/radix];
void F() throws FileNotFoundException{
Scanner in = new Scanner(new FileReader("a.txt"));
while(in.hasNextInt()){
int n = in.nextInt();
bitfield[n/radix] |= (1 << (n%radix));
}
for(int i = 0; i< bitfield.lenght; i++){
for(int j =0; j<radix; j++){
if( (bitfield[i] & (1<<j)) == 0) System.out.print(i*radix+j);
}
}
}
Fall 2: 10 MB Speicher = 10 * 10 6 * 8 Bit = 80 Millionen Bit
Lösung:
Für alle möglichen 16-Bit-Präfixe gibt es 2 16 Ganzzahlen = 65536, wir benötigen 2 16 * 4 * 8 = 2 Millionen Bits. Wir müssen 65536 Eimer bauen. Für jeden Bucket benötigen wir 4 Bytes, die alle Möglichkeiten enthalten, da im schlimmsten Fall alle 4 Milliarden Ganzzahlen zum selben Bucket gehören.
- Erstellen Sie den Zähler jedes Buckets durch den ersten Durchgang durch die Datei.
- Scannen Sie die Eimer und finden Sie den ersten, der weniger als 65536 Treffer hat.
- Erstellen Sie neue Buckets, deren hohe 16-Bit-Präfixe in Schritt 2 bis zum zweiten Durchgang der Datei gefunden werden
- Scannen Sie die in Schritt 3 eingebauten Eimer und finden Sie den ersten Eimer, der keinen Treffer hat.
Der Code ist dem obigen sehr ähnlich.
Fazit: Wir verringern den Speicher durch Erhöhen des Dateipasses.
Eine Klarstellung für Verspätete: Die gestellte Frage besagt nicht, dass genau eine Ganzzahl nicht in der Datei enthalten ist - zumindest interpretieren die meisten Leute sie nicht so. Viele Kommentare im Kommentarthread beziehen sich jedoch auf diese Variation der Aufgabe. Leider wurde der Kommentar, der ihn in den Kommentarthread eingeführt hat, später von seinem Autor gelöscht. Jetzt sieht es so aus, als hätten die verwaisten Antworten darauf einfach alles falsch verstanden. Es ist sehr verwirrend, sorry.
quelle
int getMissingNumber(File inputFile) { return 4; }
( Referenz )Antworten:
Angenommen, "Ganzzahl" bedeutet 32 Bit : 10 MB Speicherplatz sind mehr als genug, um zu zählen, wie viele Zahlen in der Eingabedatei mit einem bestimmten 16-Bit-Präfix für alle möglichen 16-Bit-Präfixe in einem Durchgang vorhanden sind Eingabedatei. Mindestens einer der Eimer wurde weniger als 2 16 Mal getroffen. Führen Sie einen zweiten Durchgang durch, um herauszufinden, welche der möglichen Nummern in diesem Bucket bereits verwendet werden.
Wenn es mehr als 32 Bit bedeutet, aber immer noch eine begrenzte Größe hat : Gehen Sie wie oben beschrieben vor und ignorieren Sie alle Eingabenummern, die zufällig außerhalb des 32-Bit-Bereichs (vorzeichenbehaftet oder vorzeichenlos; Ihrer Wahl) liegen.
Wenn "Ganzzahl" eine mathematische Ganzzahl bedeutet : Lesen Sie die Eingabe einmal durch und verfolgen Sie die
größteZahlenlänge der längsten Zahl, die Sie jemals gesehen haben. Wenn Sie fertig sind, geben Siedas Maximum plus einsals Zufallszahl mit einer weiteren Ziffer aus. (Eine der Zahlen in der Datei kann ein Bignum sein, für dessen genaue Darstellung mehr als 10 MB erforderlich sind. Wenn es sich bei der Eingabe jedoch um eine Datei handelt, können Sie zumindest die Länge von allem darstellen, was in die Datei passt.)quelle
Statistisch informierte Algorithmen lösen dieses Problem mit weniger Durchgängen als deterministische Ansätze.
Wenn sehr große Ganzzahlen zulässig sind, kann eine Zahl generiert werden, die in O (1) -Zeit wahrscheinlich eindeutig ist. Eine pseudozufällige 128-Bit-Ganzzahl wie eine GUID kollidiert nur in weniger als einer von 64 Milliarden Milliarden Fällen mit einer der vorhandenen vier Milliarden Ganzzahlen in der Menge.
Wenn Ganzzahlen auf 32 Bit begrenzt sind, kann mit weniger als 10 MB eine Zahl generiert werden, die wahrscheinlich in einem einzigen Durchgang eindeutig ist. Die Wahrscheinlichkeit, dass eine pseudozufällige 32-Bit-Ganzzahl mit einer der 4 Milliarden vorhandenen Ganzzahlen kollidiert, liegt bei 93% (4e9 / 2 ^ 32). Die Wahrscheinlichkeit, dass 1000 pseudozufällige ganze Zahlen kollidieren, beträgt weniger als eine von 12.000 Milliarden Milliarden Milliarden (Wahrscheinlichkeit einer Kollision ^ 1000). Wenn also ein Programm eine Datenstruktur mit 1000 Pseudozufallskandidaten beibehält und die bekannten Ganzzahlen durchläuft, wodurch Übereinstimmungen aus den Kandidaten eliminiert werden, ist es so gut wie sicher, mindestens eine Ganzzahl zu finden, die nicht in der Datei enthalten ist.
quelle
Eine ausführliche Diskussion über dieses Problem wird in diskutiert Jon Bentley "Spalte 1. Cracking the Oyster" Programmieren Pearls Addison-Wesley pp.3-10
Bentley diskutiert verschiedene Ansätze, einschließlich externer Sortierung, Zusammenführungssortierung unter Verwendung mehrerer externer Dateien usw. Die beste Methode, die Bentley vorschlägt, ist ein Single-Pass-Algorithmus unter Verwendung von Bitfeldern , den er humorvoll "Wonder Sort" nennt :) Kommen wir zum Problem, 4 Milliarden Zahlen können dargestellt werden in:
Der Code zum Implementieren des Bitsets ist einfach: (von der Lösungsseite entnommen )
Der Bentley-Algorithmus führt einen einzelnen Durchlauf durch die Datei durch,
set
tippt das entsprechende Bit im Array und untersucht dieses Array dann mithilfe destest
obigen Makros, um die fehlende Nummer zu finden.Wenn der verfügbare Speicher weniger als 0,466 GB beträgt, schlägt Bentley einen k-Pass-Algorithmus vor, der die Eingabe je nach verfügbarem Speicher in Bereiche unterteilt. Um ein sehr einfaches Beispiel zu nennen: Wenn nur 1 Byte (dh Speicher für 8 Zahlen) verfügbar war und der Bereich zwischen 0 und 31 lag, teilen wir dies in Bereiche von 0 bis 7, 8-15, 16-22 usw. auf und behandeln Sie diesen Bereich in jedem
32/8 = 4
Durchgang.HTH.
quelle
!= -1
die auf einem einzelnen Kern laufende Speicherbandbreite noch sättigt (dies ist SIMD innerhalb eines Registers, SWAR, mit Bits als Elementen). (Für aktuelle Intel / AMD-Designs). Sie müssen erst herausfinden, welches Bit nicht gesetzt ist, nachdem Sie den 64-Bit-Speicherort gefunden haben, der es enthält. (Und dafür können Sienot / lzcnt
.) Fair Point, dass das Durchlaufen eines Einzelbit-Tests möglicherweise nicht gut optimiert wird.Da das Problem nicht angibt, dass wir die kleinstmögliche Nummer finden müssen, die nicht in der Datei enthalten ist, können wir einfach eine Nummer generieren, die länger als die Eingabedatei selbst ist. :) :)
quelle
int
handelt sich um32
Bits, die nur ausgegeben werden2^64-1
. Erledigt.tr -d '\n' < nums.txt > new_num.txt
Für die 1 GB RAM-Variante können Sie einen Bitvektor verwenden. Sie müssen 4 Milliarden Bits == 500 MB Byte-Array zuweisen. Setzen Sie für jede Zahl, die Sie vom Eingang lesen, das entsprechende Bit auf '1'. Wenn Sie fertig sind, durchlaufen Sie die Bits und suchen Sie die erste, die noch '0' ist. Sein Index ist die Antwort.
quelle
bitSet.nextClearBit(0)
Wenn es sich um 32-Bit-Ganzzahlen handelt (wahrscheinlich aus der Auswahl von ~ 4 Milliarden Zahlen nahe 2 32 ), nimmt Ihre Liste mit 4 Milliarden Zahlen höchstens 93% der möglichen Ganzzahlen ein (4 * 10 9 / (2 32 )). ). Wenn Sie also ein Bit-Array von 2 32 Bit erstellen, wobei jedes Bit auf Null initialisiert ist (was 2 29 Byte ~ 500 MB RAM beansprucht; denken Sie an ein Byte = 2 3 Bit = 8 Bit), lesen Sie Ihre Ganzzahlliste und durch für jeden int setze das entsprechende Bit-Array-Element von 0 auf 1; Lesen Sie dann Ihr Bit-Array durch und geben Sie das erste Bit zurück, das noch 0 ist.
Wenn Sie weniger RAM (~ 10 MB) haben, muss diese Lösung leicht modifiziert werden. 10 MB ~ 83886080 Bit reichen immer noch aus, um ein Bit-Array für alle Zahlen zwischen 0 und 83886079 zu erstellen. Sie können also Ihre Liste der Ints durchlesen. und zeichnen Sie nur #s auf, die zwischen 0 und 83886079 in Ihrem Bit-Array liegen. Wenn die Zahlen zufällig verteilt sind; mit überwältigender Wahrscheinlichkeit (es unterscheidet sich um 100% um etwa 10 -2592069 ) finden Sie ein fehlendes int). Wenn Sie nur die Nummern 1 bis 2048 (mit nur 256 Byte RAM) auswählen, wird eine fehlende Nummer immer noch einen überwältigenden Prozentsatz (99,999999999999999999999999999999999999999999999999999999999999999999%) der Zeit finden.
Aber sagen wir, anstatt ungefähr 4 Milliarden Zahlen zu haben; Sie hatten ungefähr 2 32 - 1 Nummern und weniger als 10 MB RAM; Daher hat jeder kleine Bereich von Ints nur eine geringe Möglichkeit, die Zahl nicht zu enthalten.
Wenn Sie garantiert hätten, dass jedes int in der Liste eindeutig ist, könnten Sie die Zahlen summieren und die Summe mit einem fehlenden # von der vollen Summe (½) (2 32 ) (2 32 - 1) = 9223372034707292160 subtrahieren, um das fehlende int zu finden . Wenn jedoch zweimal ein int aufgetreten ist, schlägt diese Methode fehl.
Sie können jedoch immer teilen und erobern. Eine naive Methode wäre, das Array durchzulesen und die Anzahl der Zahlen in der ersten Hälfte (0 bis 2 31 -1) und der zweiten Hälfte (2 31 , 2 32 ) zu zählen. Wählen Sie dann den Bereich mit weniger Zahlen und wiederholen Sie die Aufteilung dieses Bereichs in zwei Hälften. (Wenn in (2 31 , 2 32 ) zwei weniger Zahlen enthalten wären, würde Ihre nächste Suche die Zahlen im Bereich (2 31 , 3 * 2 30 -1), (3 * 2 30 , 2 32 ) zählen. Behalten Wiederholen, bis Sie einen Bereich mit Nullzahlen gefunden haben und Ihre Antwort haben. Sollte O (lg N) ~ 32 Lesevorgänge durch das Array dauern.
Diese Methode war ineffizient. Wir verwenden nur zwei Ganzzahlen in jedem Schritt (oder ungefähr 8 Bytes RAM mit einer 4-Byte-Ganzzahl (32-Bit)). Eine bessere Methode wäre die Aufteilung in sqrt (2 32 ) = 2 16 = 65536 Bins mit jeweils 65536 Zahlen in einem Bin. Jeder Bin benötigt 4 Bytes, um seine Anzahl zu speichern, also benötigen Sie 2 18 Bytes = 256 kB. Also ist Bin 0 (0 bis 65535 = 2 16 -1), Bin 1 ist (2 16 = 65536 bis 2 * 2 16 -1 = 131071), Bin 2 ist (2 * 2 16 = 131072 bis 3 * 2 16 - 1 = 196607). In Python hätten Sie so etwas wie:
Lesen Sie die ~ 4 Milliarden Ganzzahlliste durch. und zählen Sie, wie viele Ints in jeden der 2 16 Bins fallen, und finden Sie einen unvollständigen_Bin, der nicht alle 65536-Nummern enthält. Dann lesen Sie die 4-Milliarden-Integer-Liste erneut durch. Beachten Sie diesmal jedoch nur, wenn ganze Zahlen in diesem Bereich liegen. ein bisschen umdrehen, wenn Sie sie finden.
quelle
Warum es so kompliziert machen? Sie fragen nach einer Ganzzahl, die in der Datei nicht vorhanden ist?
Gemäß den angegebenen Regeln müssen Sie nur die größte Ganzzahl speichern, die Sie bisher in der Datei gefunden haben. Geben Sie nach dem Lesen der gesamten Datei eine größere Zahl 1 zurück.
Es besteht kein Risiko, Maxint oder etwas anderes zu treffen, da gemäß den Regeln keine Einschränkung hinsichtlich der Größe der Ganzzahl oder der vom Algorithmus zurückgegebenen Zahl besteht.
quelle
Dies kann mit einer Variante der binären Suche auf sehr kleinem Raum gelöst werden.
Beginnen Sie mit dem zulässigen Zahlenbereich
0
bis4294967295
.Berechnen Sie den Mittelpunkt.
Durchlaufen Sie die Datei und zählen Sie, wie viele Zahlen gleich oder kleiner als der Mittelpunkt waren.
Wenn keine Zahlen gleich waren, sind Sie fertig. Die Mittelpunktnummer ist die Antwort.
Andernfalls wählen Sie den Bereich mit den wenigsten Zahlen und wiederholen Sie ab Schritt 2 mit diesem neuen Bereich.
Dies erfordert bis zu 32 lineare Scans durch die Datei, benötigt jedoch nur wenige Byte Speicher zum Speichern des Bereichs und der Anzahl.
Dies entspricht im Wesentlichen der Lösung von Henning , außer dass zwei Behälter anstelle von 16 KB verwendet werden.
quelle
BEARBEITEN Ok, dies war nicht ganz durchdacht, da davon ausgegangen wird, dass die Ganzzahlen in der Datei einer statischen Verteilung folgen. Anscheinend müssen sie nicht, aber selbst dann sollte man dies versuchen:
Es gibt 4,3 Milliarden 32-Bit-Ganzzahlen. Wir wissen nicht, wie sie in der Datei verteilt sind, aber der schlimmste Fall ist der mit der höchsten Shannon-Entropie: eine gleichmäßige Verteilung. In diesem Fall ist es wahrscheinlich, dass eine Ganzzahl nicht in der Datei vorkommt
((2³²-1) / 2³²) 4 ⁰⁰⁰ ⁰⁰⁰ ⁰⁰⁰ ≈ .4
Je niedriger die Shannon-Entropie ist, desto höher wird diese Wahrscheinlichkeit im Durchschnitt, aber selbst für diesen schlimmsten Fall haben wir eine Chance von 90%, nach 5 Vermutungen mit zufälligen ganzen Zahlen eine nicht vorkommende Zahl zu finden. Erstellen Sie solche Zahlen einfach mit einem Pseudozufallsgenerator und speichern Sie sie in einer Liste. Lesen Sie dann int nach int und vergleichen Sie es mit all Ihren Vermutungen. Wenn es eine Übereinstimmung gibt, entfernen Sie diesen Listeneintrag. Nachdem Sie die gesamte Datei durchgesehen haben, haben Sie wahrscheinlich noch mehr als eine Vermutung. Verwenden Sie einen von ihnen. In dem seltenen Fall (10% sogar im schlimmsten Fall), in dem keine Vermutung mehr besteht, erhalten Sie einen neuen Satz zufälliger Ganzzahlen, diesmal vielleicht mehr (10-> 99%).
Speicherverbrauch: einige Dutzend Bytes, Komplexität: O (n), Overhead: Nukleierbar, da die meiste Zeit für unvermeidbare Festplattenzugriffe aufgewendet wird, anstatt Ints zu vergleichen.
Der tatsächliche schlimmste Fall, wenn wir nicht ist eine statische Verteilung annehmen, dass jede Zahl max auftritt. einmal, weil dann nur 1 - 4000000000/2³² ≈ 6% aller ganzen Zahlen nicht in der Datei vorkommen. Sie brauchen also noch einige Vermutungen, aber das kostet immer noch keine schädlichen Mengen an Speicher.
quelle
Wenn im Bereich [0, 2 ^ x - 1] eine Ganzzahl fehlt, xor sie einfach alle zusammen. Zum Beispiel:
(Ich weiß, dass dies die Frage nicht genau beantwortet , aber es ist eine gute Antwort auf eine sehr ähnliche Frage.)
quelle
0 ^ 1 ^ 3 ^ 4 ^ 6 ^ 7
ist 0. [ Schreiben von 2 x für 2 bis x'te Potenz und a ^ b für a xor b, das xor aller k <2 x ist Null - k ^ ~ k = (2 ^ x) - 1 für k <2 ^ (x-1) und k ^ ~ k ^ j ^ ~ j = 0, wenn j = k + 2 ** (x-2) - also ist das xor aller bis auf eine Zahl der Wert des Vermissten]Sie suchen möglicherweise nach einem probabilistischen Bloom-Filter, der sehr effizient absolut bestimmen kann, ob ein Wert nicht Teil einer großen Menge ist (aber nur mit hoher Wahrscheinlichkeit feststellen kann, dass er Mitglied der Menge ist).
quelle
Basierend auf dem aktuellen Wortlaut der ursprünglichen Frage lautet die einfachste Lösung:
Suchen Sie den Maximalwert in der Datei und fügen Sie 1 hinzu.
quelle
Verwenden Sie a
BitSet
. 4 Milliarden Ganzzahlen (unter der Annahme von bis zu 2 ^ 32 Ganzzahlen), die mit 8 pro Byte in ein BitSet gepackt werden, sind 2 ^ 32/2 ^ 3 = 2 ^ 29 = ca. 0,5 GB.Um ein bisschen mehr Details hinzuzufügen - setzen Sie jedes Mal, wenn Sie eine Zahl lesen, das entsprechende Bit im BitSet. Führen Sie dann einen Durchlauf über das BitSet durch, um die erste Nummer zu finden, die nicht vorhanden ist. In der Tat können Sie dies genauso effektiv tun, indem Sie wiederholt eine Zufallszahl auswählen und testen, ob sie vorhanden ist.
Tatsächlich teilt Ihnen BitSet.nextClearBit (0) das erste nicht gesetzte Bit mit.
Wenn Sie sich die BitSet-API ansehen, scheint sie nur 0..MAX_INT zu unterstützen, sodass Sie möglicherweise 2 BitSets benötigen - eines für + fünf Nummern und eines für nicht vorhandene Nummern -, aber die Speicheranforderungen ändern sich nicht.
quelle
BitSet
versuchen Sie es mit einem Array von Bits. Tut das gleiche;)Wenn es keine Größenbeschränkung gibt, können Sie am schnellsten die Länge der Datei ermitteln und die Länge der Datei + 1 Anzahl zufälliger Ziffern (oder nur "11111 ...") generieren. Vorteil: Sie müssen die Datei nicht einmal lesen und können den Speicherbedarf auf nahezu Null reduzieren. Nachteil: Sie drucken Milliarden von Ziffern.
Wenn jedoch der einzige Faktor die Minimierung der Speichernutzung wäre und nichts anderes wichtig ist, wäre dies die optimale Lösung. Es könnte sogar zu einer Auszeichnung für den "schlimmsten Missbrauch der Regeln" führen.
quelle
Wenn wir davon ausgehen, dass der Zahlenbereich immer 2 ^ n ist (eine gerade Potenz von 2), dann exklusiv - oder funktioniert (wie auf einem anderen Poster gezeigt). Was den Grund angeht, beweisen wir es:
Die Theorie
Bei einem beliebigen 0-basierten Bereich von Ganzzahlen, bei dem
2^n
Elemente mit einem Element fehlen, können Sie dieses fehlende Element finden, indem Sie einfach die bekannten Werte zusammen xorieren, um die fehlende Zahl zu erhalten.Der Beweis
Schauen wir uns n = 2 an. Für n = 2 können wir 4 eindeutige ganze Zahlen darstellen: 0, 1, 2, 3. Sie haben ein Bitmuster von:
Wenn wir jetzt schauen, wird jedes einzelne Bit genau zweimal gesetzt. Da es eine gerade Anzahl von Malen gesetzt ist und Exklusiv-oder der Nummern 0 ergibt, ergibt das Exklusiv-Oder eine Nummer, die, wenn Exklusiv mit der fehlenden Nummer angegeben wird, ergibt 0. Daher sind die fehlende Nummer und die resultierende exklusive Nummer genau gleich. Wenn wir 2 entfernen, ist das resultierende xor
10
(oder 2).Schauen wir uns nun n + 1 an. Rufen wir an, wie oft jedes Bit gesetzt
n
istx
und wie oft jedes Bit gesetzt istn+1
y
. Der Wert vony
ist gleich,y = x * 2
weil esx
Elemente gibt, bei denen dasn+1
Bit auf 0 gesetzt ist, undx
Elemente, bei denen dasn+1
Bit auf 1 gesetzt ist. Und da2x
immer gerade ist,n+1
wird jedes Bit immer gerade gesetzt.Da
n=2
funktioniert undn+1
funktioniert, funktioniert die xor-Methode daher für alle Werte vonn>=2
.Der Algorithmus für 0-basierte Bereiche
Das ist ganz einfach. Es werden 2 * n Speicherbits verwendet, sodass für jeden Bereich <= 32 2 32-Bit-Ganzzahlen funktionieren (wobei der vom Dateideskriptor belegte Speicher ignoriert wird). Und es macht einen einzigen Durchgang der Datei.
Der Algorithmus für willkürlich basierte Bereiche
Dieser Algorithmus funktioniert für Bereiche von beliebiger Startnummer bis zu beliebiger Endzahl, solange der Gesamtbereich gleich 2 ^ n ist. Dadurch wird der Bereich grundsätzlich so neu aufgebaut, dass das Minimum bei 0 liegt. Es sind jedoch 2 Durchgänge erforderlich durch die Datei (die erste, um das Minimum zu erreichen, die zweite, um das fehlende int zu berechnen).
Beliebige Bereiche
Wir können diese modifizierte Methode auf eine Reihe beliebiger Bereiche anwenden, da alle Bereiche mindestens einmal eine Potenz von 2 ^ n überschreiten. Dies funktioniert nur, wenn ein einzelnes Bit fehlt. Es dauert 2 Durchgänge einer unsortierten Datei, aber jedes Mal wird die einzelne fehlende Nummer gefunden:
Grundsätzlich wird der Bereich um 0 neu berechnet. Anschließend wird die Anzahl der unsortierten Werte gezählt, die beim Berechnen des Exklusiv-Oder angehängt werden sollen. Dann addiert es 1 zur Anzahl der unsortierten Werte, um den fehlenden Wert zu beheben (zählen Sie den fehlenden). Dann xoring den n-Wert, der jedes Mal um 1 erhöht wird, bis n eine Potenz von 2 ist. Das Ergebnis wird dann wieder auf die ursprüngliche Basis zurückgesetzt. Erledigt.
Hier ist der Algorithmus, den ich in PHP getestet habe (unter Verwendung eines Arrays anstelle einer Datei, aber mit demselben Konzept):
In einem Array mit einem beliebigen Wertebereich (ich habe getestet, einschließlich Negative) mit einem Wert innerhalb dieses Bereichs, der fehlt, wurde jedes Mal der richtige Wert gefunden.
Ein anderer Ansatz
Warum nicht einfach nach einer Lücke suchen, da wir die externe Sortierung verwenden können? Wenn wir davon ausgehen, dass die Datei vor dem Ausführen dieses Algorithmus sortiert ist:
quelle
sum(0..n) = n*(n+1)/2
. Alsomissing = nmax*(nmax+1)/2 - nmin*(nmin+1)/2 - sum(input[])
. (Summenidee aus @ Hammars Antwort.)Trickfrage, es sei denn, sie wurde falsch zitiert. Lesen Sie die Datei einfach einmal durch, um die maximale Ganzzahl zu erhalten
n
, und kehren Sie zurückn+1
.Natürlich benötigen Sie einen Sicherungsplan, falls
n+1
ein Ganzzahlüberlauf auftritt.quelle
Überprüfen Sie die Größe der Eingabedatei und geben Sie eine beliebige Zahl aus, die zu groß ist, um von einer Datei dieser Größe dargestellt zu werden. Dies mag wie ein billiger Trick erscheinen, aber es ist eine kreative Lösung für ein Interviewproblem, es umgeht das Speicherproblem ordentlich und es ist technisch gesehen O (n).
Sollte 10 Bitcount drucken - 1 , was immer größer als 2 Bitcount ist . Technisch gesehen ist die Zahl, die Sie schlagen müssen, 2 Bitcount - (4 * 10 9 - 1) , da Sie wissen, dass die Datei (4 Milliarden - 1) andere Ganzzahlen enthält, und selbst bei perfekter Komprimierung nehmen sie mindestens Platz ein jeweils ein Bit.
quelle
Console.Write( 1 << bitcount )
statt der Schleife? Wenn die Datei n Bits enthält, ist jede (_n_ + 1) -Bit-Zahl mit einer führenden 1 absolut größer.<<
Operator nur 32-Bit-Ints zuzulassen . In beiden Fällen ist die Dateigröße sehr gering, es sei denn, Sie rollen Ihren eigenen gigantischen Integer-Typ. Demo: rextester.com/BLETJ59067Der einfachste Ansatz besteht darin, die Mindestanzahl in der Datei zu ermitteln und 1 weniger zurückzugeben. Dies verwendet O (1) -Speicher und O (n) -Zeit für eine Datei mit n Zahlen. Es schlägt jedoch fehl, wenn der Nummernkreis begrenzt ist, was dazu führen kann, dass min-1 keine Nummer ist.
Die einfache und unkomplizierte Methode zur Verwendung einer Bitmap wurde bereits erwähnt. Diese Methode verwendet O (n) Zeit und Speicher.
Ein 2-Pass-Verfahren mit 2 ^ 16 Zähleimern wurde ebenfalls erwähnt. Es liest 2 * n Ganzzahlen, verwendet also O (n) Zeit und O (1) Speicher, kann jedoch keine Datensätze mit mehr als 2 ^ 16 Zahlen verarbeiten. Es kann jedoch leicht auf (z. B.) 2 ^ 60 64-Bit-Ganzzahlen erweitert werden, indem 4 Durchgänge anstelle von 2 ausgeführt werden, und es kann leicht an die Verwendung von winzigem Speicher angepasst werden, indem nur so viele Fächer verwendet werden, wie in den Speicher passen, und die Anzahl der Durchgänge entsprechend erhöht wird In diesem Fall ist die Laufzeit nicht mehr O (n), sondern O (n * log n).
Die bisher von rfrankel und ausführlich von ircmaxell erwähnte Methode zum XOR'en aller Zahlen zusammen beantwortet die in gestellte Frage Stapelüberlauf Nr. 35185 , wie ltn100 hervorhob. Es verwendet O (1) Speicher und O (n) Laufzeit. Wenn wir momentan 32-Bit-Ganzzahlen annehmen, hat XOR eine Wahrscheinlichkeit von 7%, eine eindeutige Zahl zu erzeugen. Begründung: gegeben ~ 4G verschiedene Zahlen XOR'd zusammen, und ca. 300M nicht in der Datei, die Anzahl der gesetzten Bits an jeder Bitposition hat die gleiche Chance, ungerade oder gerade zu sein. Somit haben 2 ^ 32 Zahlen die gleiche Wahrscheinlichkeit, als XOR-Ergebnis aufzutreten, von denen 93% bereits in der Datei sind. Beachten Sie, dass die Erfolgswahrscheinlichkeit der XOR-Methode steigt, wenn die Zahlen in der Datei nicht alle unterschiedlich sind.
quelle
Aus irgendeinem Grund dachte ich, sobald ich dieses Problem las, an Diagonalisierung. Ich gehe von beliebig großen ganzen Zahlen aus.
Lesen Sie die erste Nummer. Füllen Sie es mit null Bit nach links, bis Sie 4 Milliarden Bit haben. Wenn das erste (höherwertige) Bit 0 ist, wird 1 ausgegeben; sonst wird 0 ausgegeben. (Sie müssen nicht wirklich das linke Feld auffüllen: Sie geben nur eine 1 aus, wenn die Zahl nicht genügend Bits enthält.) Machen Sie dasselbe mit der zweiten Zahl, außer dass Sie das zweite Bit verwenden. Fahren Sie auf diese Weise mit der Datei fort. Sie geben jeweils eine Bit-Nummer mit 4 Milliarden Bit aus, und diese Nummer stimmt nicht mit der in der Datei überein. Beweis: Es war das gleiche wie die n-te Zahl, dann würden sie sich auf das n-te Bit einigen, aber sie sind nicht konstruktionsbedingt.
quelle
i
dritte Bit zu verzweigen, einfach 4 Milliarden Mal 1 Bit ausgeben und am Ende eine zusätzliche 1 werfen könnten. Ich bin damit einverstanden, beliebig große Ganzzahlen im Algorithmus zu haben, aber ich denke, das Problem besteht darin, eine fehlende 32-Bit-Ganzzahl auszugeben. Anders macht es einfach keinen Sinn.Sie können Bit-Flags verwenden, um zu markieren, ob eine Ganzzahl vorhanden ist oder nicht.
Scannen Sie nach dem Durchlaufen der gesamten Datei jedes Bit, um festzustellen, ob die Nummer vorhanden ist oder nicht.
Angenommen, jede Ganzzahl ist 32 Bit, passen sie bequem in 1 GB RAM, wenn die Bit-Kennzeichnung erfolgt.
quelle
Von Reddit von Carbonetc.
quelle
Der Vollständigkeit halber ist hier eine weitere sehr einfache Lösung, deren Ausführung höchstwahrscheinlich sehr lange dauern wird, die jedoch nur sehr wenig Speicher benötigt.
Alle möglichen Ganzzahlen seien der Bereich von
int_min
bisint_max
undbool isNotInFile(integer)
eine Funktion, die true zurückgibt, wenn die Datei keine bestimmte Ganzzahl und false else enthält (indem diese bestimmte Ganzzahl mit jeder Ganzzahl in der Datei verglichen wird).quelle
isNotInFile
Funktion. Bitte stellen Sie sicher, dass Sie die Frage verstanden haben, bevor Sie sie beantworten.Für die 10-MB-Speicherbeschränkung:
Wenn Sie fertig sind, nehmen Sie einfach einen Pfad, der zuvor noch nicht erstellt wurde, um die angeforderte Nummer zu erstellen.
4 Milliarden Anzahl = 2 ^ 32, was bedeutet, dass 10 MB möglicherweise nicht ausreichen.
BEARBEITEN
Eine Optimierung ist möglich, wenn zwei Endblätter erstellt wurden und ein gemeinsames übergeordnetes Element haben, können sie entfernt und das übergeordnete Element als keine Lösung gekennzeichnet werden. Dies schneidet Zweige und reduziert den Speicherbedarf.
EDIT II
Es ist auch nicht erforderlich, den Baum vollständig zu bauen. Sie müssen nur tiefe Zweige erstellen, wenn die Zahlen ähnlich sind. Wenn wir auch Zweige schneiden, könnte diese Lösung tatsächlich funktionieren.
quelle
Ich werde die 1 GB Version beantworten:
Die Frage enthält nicht genügend Informationen, daher werde ich zunächst einige Annahmen treffen:
Die Ganzzahl beträgt 32 Bit mit einem Bereich von -2.147.483.648 bis 2.147.483.647.
Pseudocode:
quelle
Solange wir kreative Antworten geben, ist hier eine andere.
Verwenden Sie das externe Sortierprogramm, um die Eingabedatei numerisch zu sortieren. Dies funktioniert für jede Menge Speicher, die Sie möglicherweise haben (bei Bedarf wird Dateispeicher verwendet). Lesen Sie die sortierte Datei durch und geben Sie die erste fehlende Nummer aus.
quelle
Bit-Eliminierung
Eine Möglichkeit besteht darin, Bits zu eliminieren, dies führt jedoch möglicherweise nicht zu einem Ergebnis (wahrscheinlich nicht). Pseudocode:
Bitzählungen
Verfolgen Sie die Anzahl der Bits. und verwenden Sie die Bits mit den geringsten Beträgen, um einen Wert zu generieren. Auch dies hat keine Garantie für die Erzeugung eines korrekten Wertes.
Bereichslogik
Verfolgen Sie eine Liste der geordneten Bereiche (sortiert nach Start). Ein Bereich wird durch die Struktur definiert:
Gehen Sie jeden Wert in der Datei durch und versuchen Sie, ihn aus dem aktuellen Bereich zu entfernen. Diese Methode hat keine Speichergarantien, sollte aber ziemlich gut funktionieren.
quelle
2 128 * 10 18 + 1 (was (2 8 ) 16 * 10 18 + 1 ist) - kann es für heute keine universelle Antwort sein? Dies stellt eine Zahl dar, die nicht in einer 16-EB-Datei gespeichert werden kann. Dies ist die maximale Dateigröße in einem aktuellen Dateisystem.
quelle
Ich denke, dies ist ein gelöstes Problem (siehe oben), aber es gibt einen interessanten Nebenfall, den man beachten sollte, weil er möglicherweise gefragt wird:
Wenn genau 4.294.967.295 (2 ^ 32 - 1) 32-Bit-Ganzzahlen ohne Wiederholungen vorhanden sind und daher nur eine fehlt, gibt es eine einfache Lösung.
Starten Sie eine laufende Summe bei Null und fügen Sie für jede Ganzzahl in der Datei diese Ganzzahl mit 32-Bit-Überlauf hinzu (effektiv runningTotal = (runningTotal + nextInteger)% 4294967296). Wenn Sie fertig sind, fügen Sie 4294967296/2 zur laufenden Summe hinzu, erneut mit 32-Bit-Überlauf. Subtrahieren Sie dies von 4294967296, und das Ergebnis ist die fehlende Ganzzahl.
Das Problem "nur eine fehlende Ganzzahl" kann mit nur einem Lauf und nur 64 Bit RAM für die Daten gelöst werden (32 für die laufende Summe, 32 zum Einlesen der nächsten Ganzzahl).
Folgerung: Die allgemeinere Spezifikation ist extrem einfach anzupassen, wenn es uns nicht darum geht, wie viele Bits das ganzzahlige Ergebnis haben muss. Wir generieren nur eine Ganzzahl, die groß genug ist, dass sie nicht in der angegebenen Datei enthalten sein kann. Auch dies beansprucht absolut minimalen RAM. Siehe den Pseudocode.
quelle
Wie Ryan es im Grunde gesagt hat, sortiere die Datei und gehe dann die ganzen Zahlen durch und wenn ein Wert dort übersprungen wird, hast du ihn :)
EDIT bei downvoters: die OP erwähnt , dass die Datei sortiert werden, so dies eine gültige Methode.
quelle
Wenn Sie die 32-Bit-Einschränkung nicht annehmen, geben Sie einfach eine zufällig generierte 64-Bit-Zahl zurück (oder 128-Bit, wenn Sie ein Pessimist sind). Die Wahrscheinlichkeit einer Kollision beträgt
1 in 2^64/(4*10^9) = 4611686018.4
(ungefähr 1 von 4 Milliarden). Du hättest die meiste Zeit Recht!(Scherz ... irgendwie.)
quelle