it-swarm.com.de

Finden Sie eine ganze Zahl, die nicht unter vier Milliarden liegt

Es ist eine Interviewfrage:

Geben Sie bei einer Eingabedatei mit vier Milliarden Ganzzahlen einen Algorithmus an, um eine Ganzzahl zu generieren, die nicht in der Datei enthalten ist. Angenommen, Sie haben 1 GB Speicher. Folgen Sie dem, was Sie tun würden, wenn Sie nur 10 MB Arbeitsspeicher hätten.

Meine Analyse:

Die Größe der Datei beträgt 4 × 109× 4 Bytes = 16 GB.

Wir können extern sortieren und so den Bereich der ganzen Zahlen kennenlernen. Meine Frage ist, wie man die fehlende Ganzzahl in den sortierten großen Ganzzahlensätzen am besten erkennt.

Mein Verständnis (nach dem Lesen aller Antworten):

Angenommen, es handelt sich um 32-Bit-Ganzzahlen. Es gibt 2 ^ 32 = 4 * 109 verschiedene ganze Zahlen.

Fall 1: Wir haben 1 GB = 1 * 109 * 8 Bits = 8 Milliarden Bits Speicher.

Lösung: Wenn wir ein Bit verwenden, das eine bestimmte Ganzzahl darstellt, reicht es 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 * 106 * 8 Bits = 80 Millionen Bits

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 mit allen Möglichkeiten, da im schlimmsten Fall alle 4 Milliarden Ganzzahlen zum selben Bucket gehören.

  1. Erstellen Sie den Zähler für jeden Bucket im ersten Durchgang durch die Datei.
  2. Scannen Sie die Eimer und finden Sie den ersten, der weniger als 65536 Treffer hat.
  3. Erstellen Sie neue Buckets, deren hohe 16-Bit-Präfixe im zweiten Durchgang der Datei in Schritt 2 ermittelt wurden
  4. Scannen Sie die in Schritt 3 gebauten Eimer und finden Sie den ersten Eimer, der keinen Treffer hat.

Der Code ist dem obigen sehr ähnlich.

Fazit: Wir verringern den Speicher durch zunehmenden Dateitransfer.


Eine Klarstellung für Verspätete: Die gestellte Frage besagt nicht, dass es genau eine Ganzzahl gibt, die nicht in der Datei enthalten ist - zumindest wird sie von den meisten Menschen nicht so interpretiert. Viele Kommentare im Kommentarthread sind zu dieser Variante der Aufgabe. Leider wurde der Kommentar eingeführt zum Kommentarthread später von seinem Autor gelöscht, so dass es nun so aussieht, als hätten die verwaisten Antworten darauf einfach alles missverstanden. Das ist sehr verwirrend. Entschuldigung.

675
SecureFish

nter der Annahme, dass "Ganzzahl" 32 Bit bedeutet: 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 vorhanden sind -Bit-Präfixe in einem Durchgang durch die Eingabedatei. Mindestens einer der Eimer wurde weniger als 2 ^ 16 Mal getroffen. Führen Sie einen zweiten Durchlauf durch, um herauszufinden, welche der möglichen Nummern in diesem Eimer 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 außerhalb des 32-Bit-Bereichs (mit oder ohne Vorzeichen Ihrer Wahl) liegen.

Wenn "Ganzzahl" eine mathematische Ganzzahl bedeutet: Lesen Sie die Eingabe einmal durch und verfolgen Sie die größte Zahl Länge der längsten Nummer, die Sie je gesehen haben. Wenn Sie fertig sind, wird ausgegeben das Maximum plus eins eine Zufallszahl mit einer weiteren Ziffer. (Eine der Zahlen in der Datei kann ein Bignum sein, für dessen exakte Darstellung mehr als 10 MB erforderlich sind. Wenn es sich bei der Eingabe jedoch um eine Datei handelt, können Sie mindestens die Länge angeben. von allem, was dazu passt).

521
Henning Makholm

Statistisch fundierte Algorithmen lösen dieses Problem mit weniger Durchläufen als deterministische Ansätze.

Wenn sehr große ganze Zahlen erlaubt sind , kann man eine Zahl erzeugen, die wahrscheinlich in O(1) eindeutig ist Eine pseudozufällige 128-Bit-Ganzzahl wie ein GUID kollidiert nur mit einer der vorhandenen vier Milliarden Ganzzahlen in der Menge in weniger als einer von alle 64 Milliarden Milliarden Fälle.

Wenn ganze Zahlen auf 32 Bit begrenzt sind, kann man mit weniger als 10 MB eine Zahl generieren, die wahrscheinlich in einem 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 Pseudozufalls-Kandidaten verwaltet und die bekannten ganzen Zahlen durchläuft und dabei Übereinstimmungen mit den Kandidaten eliminiert, ist es so gut wie sicher, mindestens eine ganze Zahl zu finden, die nicht in der Datei enthalten ist.

194
Ben Haley

Eine ausführliche Diskussion zu diesem Problem wurde in Jon Bentley "Column 1. Cracking the Oyster" besprochen. Programmieren von Pearls Addison-Wesley S. 3-1

Bentley diskutiert verschiedene Ansätze, einschließlich externes Sortieren, Sortieren zusammenführen mit mehreren externen Dateien usw. Die beste Methode, die Bentley vorschlägt, ist ein Single-Pass-Algorithmus mit Bitfelder , den er humorvoll "Wonder Sort" nennt :) In Bezug auf das Problem können 4 Milliarden Zahlen dargestellt werden in:

4 billion bits = (4000000000 / 8) bytes = about 0.466 GB

Der Code zum Implementieren des Bitsets ist einfach: (entnommen aus Lösungsseite )

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];

void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); }
void clr(int i) {        a[i>>SHIFT] &= ~(1<<(i & MASK)); }
int  test(int i){ return a[i>>SHIFT] &   (1<<(i & MASK)); }

Bentleys Algorithmus durchläuft die Datei einmal, wobei set das entsprechende Bit im Array angibt, und untersucht dieses Array dann mithilfe des obigen Makros test, um die fehlende Zahl zu ermitteln.

Wenn der verfügbare Speicher weniger als 0,466 GB beträgt, schlägt Bentley einen K-Pass-Algorithmus vor, der die Eingabe in Abhängigkeit vom verfügbaren Speicher in Bereiche unterteilt. Um ein sehr einfaches Beispiel zu nehmen: 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 bis 15, 16 bis 22 usw. und behandeln Sie diesen Bereich in jedem von 32/8 = 4 geht vorbei.

HTH.

141
vine'th

Da das Problem nicht angibt, dass wir die kleinstmögliche Zahl finden müssen, die nicht in der Datei enthalten ist, können wir einfach eine Zahl generieren, die länger als die Eingabedatei selbst ist. :)

117
Andris

Für die 1 GB RAM Variante können Sie einen Bitvektor verwenden. Sie müssen 4 Milliarden Bits == 500 MB Bytearray zuweisen. Setzen Sie für jede Zahl, die Sie von der Eingabe lesen, das entsprechende Bit auf "1". Wenn Sie fertig sind, iterieren Sie über die Bits und finden Sie die erste, die immer noch "0" ist. Ihr Index ist die Antwort.

56
Itay Maman

Wenn es sich um 32-Bit-Ganzzahlen handelt (wahrscheinlich aus der Auswahl von ~ 4 Milliarden Zahlen in der Nähe von 2)32), nimmt Ihre Liste mit 4 Milliarden Zahlen höchstens 93% der möglichen ganzen Zahlen ein (4 * 109 / (232)). Also, wenn Sie ein Bit-Array von 2 erstellen32 Bits, wobei jedes Bit auf Null initialisiert ist (was 2 in Anspruch nimmt29 Bytes ~ 500 MB RAM; Merke dir ein Byte = 23 bits = 8 bits), lesen Sie Ihre Integer-Liste durch und setzen Sie für jedes int das entsprechende Bit-Array-Element von 0 bis 1; und lesen Sie dann Ihr Bit-Array durch und geben Sie das erste Bit zurück, das immer noch 0 ist.

Wenn Sie weniger RAM (~ 10 MB) haben, muss diese Lösung leicht modifiziert werden. 10 MB ~ 83886080 Bits reichen immer noch aus, um ein Bit-Array für alle Zahlen zwischen 0 zu erstellen und 83886079. Sie können also Ihre Liste von Ints durchlesen und nur Datensatznummern in Ihrem Bit-Array zwischen 0 und 83886079. Wenn die Zahlen zufällig verteilt sind, mit überwältigender Wahrscheinlichkeit (sie unterscheiden sich um 100% um ungefähr 10-2592069 ) Sie werden ein fehlendes int finden). Wenn Sie nur die Nummern 1 bis 2048 (mit nur 256 Byte RAM) auswählen, wird eine fehlende Nummer in der Tat immer noch einen überwältigenden Prozentsatz (99,999999999999999999999999999999999999999999999999999999999999999995%) der Zeit aufweisen.

Aber lassen Sie uns sagen, anstatt ungefähr 4 Milliarden Zahlen zu haben; du hattest so etwas wie 232 - 1 Nummer und weniger als 10 MB RAM; Daher besteht für einen kleinen Bereich von Ints nur eine geringe Wahrscheinlichkeit, dass die Zahl nicht enthalten ist.

Wenn Sie die Garantie hätten, dass jedes int in der Liste eindeutig ist, könnten Sie die Zahlen summieren und die Summe mit einem # subtrahieren, das zur vollen Summe (½) (2) fehlt32) (232 - 1) = 9223372034707292160, um die fehlende Ganzzahl zu finden. Wenn ein int jedoch zweimal vorkommt, schlägt diese Methode fehl.

Sie können sich jedoch immer teilen und siegen. Eine naive Methode wäre, das Array durchzulesen und die Anzahl der Zahlen in der ersten Hälfte zu zählen (0 bis 2)31-1) und zweite Hälfte (231, 232). Wählen Sie dann den Bereich mit weniger Zahlen und teilen Sie diesen Bereich erneut in zwei Hälften. (Sagen Sie, wenn in (231, 232) dann würde Ihre nächste Suche die Zahlen im Bereich (2313 * 230-1), (3 * 230, 232). Wiederholen Sie den Vorgang, bis Sie einen Bereich mit Nullen finden und Ihre Antwort erhalten. Sollte dauern O (lg N) ~ 32 liest das Array durch.

Diese Methode war ineffizient. In jedem Schritt werden nur zwei Ganzzahlen verwendet (oder ungefähr 8 Byte RAM mit einer 4-Byte-Ganzzahl (32-Bit)). Eine bessere Methode wäre die Aufteilung in sqrt (2)32) = 216 = 65536 Behälter mit jeweils 65536 Nummern in einem Behälter. Jeder Bin benötigt 4 Bytes, um seine Anzahl zu speichern. Sie benötigen also 2 Bytes18 Bytes = 256 kB. Bin 0 ist also (0 bis 65535 = 216-1) ist Fach 1 (216= 65536 bis 2 · 216-1 = 131071), Fach 2 ist (2 * 216= 131072 bis 3 · 216-1 = 196607). In python hättest du so etwas wie:

import numpy as np
nums_in_bin = np.zeros(65536, dtype=np.uint32)
for N in four_billion_int_array:
    nums_in_bin[N // 65536] += 1
for bin_num, bin_count in enumerate(nums_in_bin):
    if bin_count < 65536:
        break # we have found an incomplete bin with missing ints (bin_num)

Lesen Sie die ~ 4 Milliarden Integer-Liste durch; und zähle, wieviele Ints in jeden der 2 fallen16 Bins und finde einen incomplete_bin, der nicht alle 65536-Nummern enthält. Dann lesen Sie die 4-Milliarden-Integer-Liste noch einmal durch. Diesmal wird jedoch nur bemerkt, wenn sich ganze Zahlen in diesem Bereich befinden. ein bisschen blättern, wenn Sie sie finden.

del nums_in_bin # allow gc to free old 256kB array
from bitarray import bitarray
my_bit_array = bitarray(65536) # 32 kB
my_bit_array.setall(0)
for N in four_billion_int_array:
    if N // 65536 == bin_num:
        my_bit_array[N % 65536] = 1
for i, bit in enumerate(my_bit_array):
    if not bit:
        print bin_num*65536 + i
        break
45
dr jimbob

Warum es so kompliziert machen? Sie fragen nach einer Ganzzahl, die in der Datei nicht vorhanden ist?

Nach den angegebenen Regeln müssen Sie nur die größte Ganzzahl speichern, die Sie bisher in der Datei gefunden haben. Wenn die gesamte Datei gelesen wurde, geben Sie eine 1 größer als diese zurück.

Es besteht kein Risiko, dass maxint oder irgendetwas getroffen wird, da es gemäß den Regeln keine Einschränkung hinsichtlich der Größe der Ganzzahl oder der vom Algorithmus zurückgegebenen Zahl gibt.

37
Pete

Dies kann mit einer Variante der binären Suche auf kleinstem Raum gelöst werden.

  1. Beginnen Sie mit dem zulässigen Zahlenbereich, 0 bis 4294967295.

  2. Berechnen Sie den Mittelpunkt.

  3. Durchlaufen Sie die Datei und zählen Sie, wie viele Zahlen gleich, kleiner oder größer als der Mittelpunktwert waren.

  4. Wenn keine Zahlen gleich waren, sind Sie fertig. Die Mittelpunktzahl ist die Antwort.

  5. Wählen Sie andernfalls den Bereich mit den wenigsten Zahlen aus und wiederholen Sie den Vorgang ab Schritt 2 mit diesem neuen Bereich.

Dies erfordert bis zu 32 lineare Durchsuchungen der Datei, es werden jedoch nur wenige Bytes Speicher zum Speichern des Bereichs und der Anzahl verwendet.

Dies ist im Wesentlichen dasselbe wie Hennings Lösung , außer dass zwei Bins anstelle von 16 KB verwendet werden.

32
hammar

Wenn Sie eine Ganzzahl im Bereich [0, 2 ^ x - 1] haben, dann xor sie alle zusammen. Zum Beispiel:

>>> 0 ^ 1 ^ 3
2
>>> 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 6 ^ 7
5

(Ich weiß, dass dies die Frage nicht genau beantwortet , aber es ist eine gute Antwort auf eine sehr ähnliche Frage.)

24
rfrankel

Basierend auf dem aktuellen Wortlaut in der ursprünglichen Frage ist die einfachste Lösung:

Suchen Sie den Maximalwert in der Datei und fügen Sie 1 hinzu.

17
oosterwal

Sie suchen möglicherweise nach einer Wahrscheinlichkeitsfunktion Bloom-Filter , mit der sich sehr effizient feststellen lässt, ob ein Wert nicht Teil einer großen Menge ist, sondern nur mit hoher Wahrscheinlichkeit, ob es sich um eine Wahrscheinlichkeitsfunktion handelt Mitglied des Sets.)

16
Paul

Verwenden Sie ein BitSet. 4 Milliarden Ganzzahlen (unter der Annahme von bis zu 2 ^ 32 Ganzzahlen), die mit 8 pro Byte in ein BitSet gepackt werden, ergeben 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 prüfen, ob diese vorhanden ist.

Tatsächlich gibt BitSet.nextClearBit (0) das erste nicht gesetzte Bit an.

Betrachtet man die BitSet-API, so scheint sie nur 0..MAX_INT zu unterstützen, so dass Sie möglicherweise 2 BitSets benötigen - eines für + fünf und eines für-have-Zahlen - aber die Speicheranforderungen ändern sich nicht.

14
dty

Wenn es keine Größenbeschränkung gibt, ist es am schnellsten, die Länge der Datei zu ermitteln und die Länge der Datei + 1 Anzahl zufälliger Ziffern (oder nur "11111 ...") zu generieren. Vorteil: Sie müssen die Datei nicht einmal lesen und können den Speicherverbrauch auf nahezu Null reduzieren. Nachteil: Sie drucken Milliarden von Ziffern.

Wenn der einzige Faktor die Minimierung der Speichernutzung wäre und nichts anderes wichtig wäre, wäre dies die optimale Lösung. Es könnte sogar zu einem "schlimmsten Missbrauch der Regeln" führen.

12
vsz

Wenn wir davon ausgehen, dass der Zahlenbereich immer 2 ^ n ist (eine gerade Potenz von 2), funktioniert exclusive-or (wie auf einem anderen Poster gezeigt). Was das Warum angeht, lassen Sie es uns beweisen:

Die Theorie

Bei einem auf 0 basierenden Bereich von Ganzzahlen mit 2^n - Elementen, bei denen ein Element fehlt, 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 Ganzzahlen darstellen: 0, 1, 2, 3. Sie haben ein Bitmuster von:

  • 0 - 00
  • 1 - 01
  • 2 - 10
  • 3 - 11

Nun, wenn wir schauen, wird jedes Bit genau zweimal gesetzt. Daher ergibt das Exklusiv-Oder eine Zahl, die bei Exklusiv-Oder-Verknüpfung mit der fehlenden Zahl zu 0 führt. Wenn eine einzelne Zahl fehlt, ergibt das Exklusiv-Oder eine Zahl, die bei Exklusiv-Oder-Verknüpfung mit der fehlenden Zahl resultiert 0. Daher sind die fehlende Nummer und die sich ergebende Nummer mit exklusivem Bezug exakt gleich. Wenn wir 2 entfernen, ist das resultierende xor 10 (Oder 2).

Betrachten wir nun n + 1. Nennen wir, wie oft jedes Bit in n, x und wie oft jedes Bit in n+1y gesetzt ist. Der Wert von y ist gleich y = x * 2, Da es x Elemente gibt, bei denen das n+1 - Bit auf 0 gesetzt ist, und x Elemente Wenn das Bit n+1 auf 1 gesetzt ist. Und da 2x immer gerade ist, hat n+1 jedes Bit immer eine gerade Anzahl von Sätzen.

Daher funktioniert die xor-Methode für alle Werte von n=2, Da n+1 Und n>=2 Funktionieren.

Der Algorithmus für 0-basierte Bereiche

Das ist ganz einfach. Es verwendet 2 * n Speicherbits, 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.

long supplied = 0;
long result = 0;
while (supplied = read_int_from_file()) {
    result = result ^ supplied;
}
return result;

Der Algorithmus für willkürliche Bereiche

Dieser Algorithmus funktioniert für Bereiche von einer beliebigen Startnummer bis zu einer beliebigen Endnummer, solange der Gesamtbereich 2 ^ n beträgt. Damit wird der Bereich im Grunde auf das Minimum von 0 zurückgesetzt. Es sind jedoch 2 Durchgänge erforderlich durch die Datei (der erste, der das Minimum ermittelt, der zweite, der das fehlende int berechnet).

long supplied = 0;
long result = 0;
long offset = INT_MAX;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    result = result ^ (supplied - offset);
}
return result + offset;

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 es wird jedes Mal die einzelne fehlende Nummer gefunden:

long supplied = 0;
long result = 0;
long offset = INT_MAX;
long n = 0;
double temp;
while (supplied = read_int_from_file()) {
    if (supplied < offset) {
        offset = supplied;
    }
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
    n++;
    result = result ^ (supplied - offset);
}
// We need to increment n one value so that we take care of the missing 
// int value
n++
while (n == 1 || 0 != (n & (n - 1))) {
    result = result ^ (n++);
}
return result + offset;

Grundsätzlich wird der Bereich um 0 neu berechnet. Anschließend wird die Anzahl der nicht sortierten Werte gezählt, die beim Berechnen des Exklusiv-Oder angehängt werden sollen. Anschließend wird die Anzahl der nicht sortierten Werte um 1 erhöht, um den fehlenden Wert zu korrigieren (den fehlenden Wert zählen). Bewahren Sie dann das Xoring des Werts n auf, der jedes Mal um 1 erhöht wird, bis n eine Potenz von 2 ist. Das Ergebnis wird dann auf die ursprüngliche Basis zurückgesetzt. Getan.

Hier ist der Algorithmus, den ich in PHP (unter Verwendung eines Arrays anstelle einer Datei, aber mit demselben Konzept) getestet habe:

function find($array) {
    $offset = min($array);
    $n = 0;
    $result = 0;
    foreach ($array as $value) {
        $result = $result ^ ($value - $offset);
        $n++;
    }
    $n++; // This takes care of the missing value
    while ($n == 1 || 0 != ($n & ($n - 1))) {
        $result = $result ^ ($n++);
    }
    return $result + $offset;
}

Eingespeist in ein Array mit einem beliebigen Wertebereich (einschließlich Negativen), wobei einer innerhalb dieses Bereichs fehlt, wurde jedes Mal der richtige Wert gefunden.

Ein anderer Ansatz

Warum nicht einfach nach einer Lücke suchen, da wir eine externe Sortierung verwenden können? Wenn wir annehmen, dass die Datei vor dem Ausführen dieses Algorithmus sortiert wurde:

long supplied = 0;
long last = read_int_from_file();
while (supplied = read_int_from_file()) {
    if (supplied != last + 1) {
        return last + 1;
    }
    last = supplied;
}
// The range is contiguous, so what do we do here?  Let's return last + 1:
return last + 1;
10
ircmaxell

Ü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 sauber das Speicherproblem und es ist technisch gesehen O (n).

void maxNum(ulong filesize)
{
    ulong bitcount = filesize * 8; //number of bits in file

    for (ulong i = 0; i < bitcount; i++)
    {
        Console.Write(9);
    }
}

Sollte 10 ausgeben bitcount - 1, der immer größer als 2 ist bitcount. Technisch gesehen ist die Zahl, die du schlagen musst, bitcount - (4 * 109 - 1), da Sie wissen, dass die Datei (4 Milliarden - 1) andere Ganzzahlen enthält und diese auch bei perfekter Komprimierung jeweils mindestens ein Bit belegen.

9
Justin Morgan
  • Am einfachsten ist es, die Mindestanzahl in der Datei zu finden und 1 weniger als diese zurückzugeben. Dies verwendet O(1) Speicher und O(n) Zeit für eine Datei mit n Nummern. Wenn der Nummernbereich begrenzt ist, schlägt dies jedoch fehl. was dazu führen könnte, dass min-1 keine Zahl 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) time und O(1) storage, kann jedoch keine Datensätze mit mehr als 2 ^ 16 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 länger O(n), sondern O (n * log n).

  • Die bisher von rfrankel und ausführlich von ircmaxell erwähnte Methode zur XOR-Verknüpfung aller Zahlen beantwortet die in stackoverflow # 35185 gestellte Frage, wie ltn100 hervorhob. Es verwendet O(1) Speicher und O(n) Laufzeit. Wenn wir momentan 32-Bit-Ganzzahlen annehmen, XOR hat eine Wahrscheinlichkeit von 7%, eine eindeutige Zahl zu erzeugen. Begründung: Wenn ~ 4G eindeutige Zahlen XOR-verknüpft und ca. 300M nicht in der Datei sind, hat die Anzahl der gesetzten Bits an jeder Bitposition die gleiche Chance, ungerade zu sein oder sogar. Somit ist die Wahrscheinlichkeit, dass 2 ^ 32 Zahlen auftreten, gleich hoch wie das Ergebnis XOR, wovon 93% bereits in der Datei enthalten sind. Beachten Sie, dass, wenn die in der Datei enthaltenen Zahlen nicht alle verschieden sind, Die Erfolgswahrscheinlichkeit der XOR -Methode steigt.

Trickfrage, es sei denn, es wurde falsch zitiert. Lesen Sie die Datei einfach einmal durch, um die maximale Ganzzahl n zu erhalten, und geben Sie n+1 Zurück.

Natürlich benötigen Sie einen Backup-Plan, falls n+1 Einen Integer-Überlauf verursacht.

8
Mark Ransom

Aus irgendeinem Grund habe ich, sobald ich dieses Problem gelesen habe, an eine Diagonalisierung gedacht. Ich gehe von willkürlich großen ganzen Zahlen aus.

Lies die erste Zahl. Füllen Sie es mit Null-Bits nach links, bis Sie 4 Milliarden Bits haben. Wenn das erste (höherwertige) Bit 0 ist, wird 1 ausgegeben; sonst gib 0 aus. (Du musst nicht wirklich links auffüllen: Du gibst nur eine 1 aus, wenn die Zahl nicht genug Bits enthält.) Mach dasselbe mit der zweiten Zahl, außer du benutzt ihr zweites Bit. Fahren Sie auf diese Weise mit der Datei fort. Sie geben jeweils ein Bit mit 4 Milliarden Bits aus, und diese Zahl stimmt nicht mit der in der Datei überein. Beweis: Es war dasselbe wie die n-te Zahl, dann würden sie sich auf das n-te Bit einigen, aber sie sind nicht konstruktionsbedingt.

7

Der Vollständigkeit halber ist hier eine weitere sehr einfache Lösung, deren Ausführung sehr wahrscheinlich sehr lange dauern wird, die jedoch nur sehr wenig Speicher benötigt.

Alle möglichen Ganzzahlen sind der Bereich von int_min Bis int_max Und bool isNotInFile(integer) eine Funktion, die true zurückgibt, wenn die Datei keine bestimmte Ganzzahl enthält und false else (durch Vergleichen) diese bestimmte ganze Zahl mit jeder ganzen Zahl in der Datei)

for (integer i = int_min; i <= int_max; ++i)
{
    if (isNotInFile(i)) {
        return i;
    }
}
6
deg

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, dann passen sie bequemerweise in 1 GB RAM, wenn das Bitflaggen durchgeführt wird.

6
Shamim Hafiz

Entfernen Sie den Leerraum und nicht numerische Zeichen aus der Datei und fügen Sie 1 hinzu. Ihre Datei enthält jetzt eine einzelne Nummer, die nicht in der Originaldatei aufgeführt ist.

Von Reddit von Carbonetc.

6
Ashley

Für die 10 MB Speicherbeschränkung:

  1. Wandle die Zahl in ihre Binärdarstellung um.
  2. Erstellen Sie einen Binärbaum mit left = 0 und right = 1.
  3. Fügen Sie jede Zahl in den Baum ein, indem Sie ihre Binärdarstellung verwenden.
  4. Wenn bereits eine Nummer eingefügt wurde, wurden die Blätter bereits erstellt.

Wenn Sie fertig sind, nehmen Sie einfach einen Pfad, der noch nicht erstellt wurde, um die angeforderte Nummer zu erstellen.

4-Milliarden-Zahl = 2 ^ 32, was bedeutet, dass 10 MB möglicherweise nicht ausreichen.

EDIT

Eine Optimierung ist möglich, wenn zwei Endeblätter erstellt wurden und ein gemeinsames übergeordnetes Element haben. Sie können dann entfernt und das übergeordnete Element als keine Lösung gekennzeichnet werden. Dies schneidet Äste und reduziert den Speicherbedarf.

EDIT II

Es ist nicht nötig, den Baum komplett zu bauen. Sie müssen nur tiefe Zweige bauen, wenn die Zahlen ähnlich sind. Wenn wir auch Äste schneiden, könnte diese Lösung tatsächlich funktionieren.

5

Ich werde die 1 GB Version beantworten:

Die Frage enthält nicht genügend Informationen, daher möchte ich zunächst einige Annahmen treffen:

Die Ganzzahl ist 32 Bit mit einem Bereich von -2.147.483.648 bis 2.147.483.647.

Pseudocode:

var bitArray = new bit[4294967296];  // 0.5 GB, initialized to all 0s.

foreach (var number in file) {
    bitArray[number + 2147483648] = 1;   // Shift all numbers so they start at 0.
}

for (var i = 0; i < 4294967296; i++) {
    if (bitArray[i] == 0) {
        return i - 2147483648;
    }
}
5
BobTurbo

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 an Speicher, die Sie möglicherweise haben (bei Bedarf wird Dateispeicher verwendet). Lesen Sie die sortierte Datei durch und geben Sie die erste fehlende Zahl aus.

4
Rhialto

Wie Ryan es im Grunde gesagt hat, sortiere die Datei und gehe dann über die ganzen Zahlen und wenn ein Wert dort übersprungen wird, hast du es :)

EDIT bei Downvotern: Das OP erwähnte, dass die Datei sortiert werden könnte, so dass dies eine gültige Methode ist.

3
ratchet freak

2128 * 1018 + 1 (das ist (28)16 * 1018 + 1) - Kann es nicht eine universelle Antwort für heute sein? Dies ist eine Zahl, die in 16-EB-Dateien nicht gespeichert werden kann. Dies ist die maximale Dateigröße in einem aktuellen Dateisystem.

3

Bit Elimination

Eine Möglichkeit besteht darin, Bits zu eliminieren, dies führt jedoch möglicherweise nicht zu einem Ergebnis (wahrscheinlich wird dies nicht der Fall sein). Pseudocode:

long val = 0xFFFFFFFFFFFFFFFF; // (all bits set)
foreach long fileVal in file
{
    val = val & ~fileVal;
    if (val == 0) error;
}

Bit Counts

Verfolgen Sie die Anzahl der Bits; und verwenden Sie die Bits mit den geringsten Beträgen, um einen Wert zu erzeugen. Auch dies ist keine Garantie für die Erzeugung eines korrekten Wertes.

Bereichslogik

Verfolgen Sie eine Liste geordneter Bereiche (sortiert nach Start). Ein Bereich wird durch die Struktur definiert:

struct Range
{
  long Start, End; // Inclusive.
}
Range startRange = new Range { Start = 0x0, End = 0xFFFFFFFFFFFFFFFF };

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 ganz gut funktionieren.

3

Ich denke, dies ist ein gelöstes Problem (siehe oben), aber es gibt einen interessanten Nebenfall, den man im Hinterkopf behalten sollte, da er möglicherweise gefragt wird:

Wenn es genau 4.294.967.295 (2 ^ 32 - 1) 32-Bit-Ganzzahlen ohne Wiederholungen gibt 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 der laufenden Summe 4294967296/2 hinzu, ebenfalls mit 32-Bit-Überlauf. Subtrahieren Sie dies von 4294967296, und das Ergebnis ist die fehlende Ganzzahl.

Das Problem "nur eine fehlende Ganzzahl" ist mit nur einem Durchlauf und nur 64 Bits RAM für die Daten (32 für die laufende Summe, 32 zum Einlesen der nächsten Ganzzahl) lösbar.

Folgerung: Die allgemeinere Spezifikation ist extrem einfach abzugleichen, wenn es nicht darum geht, wie viele Bits das ganzzahlige Ergebnis haben muss. Wir generieren einfach eine ganze Zahl, die groß genug ist, um in der angegebenen Datei nicht enthalten zu sein. Auch dies beansprucht nur minimal RAM. Siehe den Pseudocode.

# Grab the file size
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
# Print a '2' for every bit of the file.
for (c=0; c<sz; c++) {
  for (b=0; b<4; b++) {
    print "2";
  }
}
3
Syntaera

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 Kollisionswahrscheinlichkeit beträgt 1 in 2^64/(4*10^9) = 4611686018.4 (ungefähr 1 zu 4 Milliarden). Sie würden die meiste Zeit Recht haben!

(Scherz ... irgendwie.)

2
Peter Gibson

Geben Sie bei einer Eingabedatei mit vier Milliarden Ganzzahlen einen Algorithmus an, um eine Ganzzahl zu generieren, die nicht in der Datei enthalten ist. Angenommen, Sie haben 1 GiB Speicher. Verfolgen Sie, was Sie tun würden, wenn Sie nur 10 MiB Speicher hätten.

Die Größe der Datei beträgt 4 * 109 * 4 Bytes = 16 GiB

Im Fall einer 32-Bit-Ganzzahl ohne Vorzeichen

0 <= Number < 2^32
0 <= Number < 4,294,967,296

Meine vorgeschlagene Lösung: C++ ohne Fehlerprüfung

#include <vector>
#include <fstream>
#include <iostream>
using namespace std;

int main ()
{
    const long SIZE = 1L << 32;

    std::vector<bool> checker(SIZE, false);

    std::ifstream infile("file.txt");  // TODO: error checking

    unsigned int num = 0;

    while (infile >> num)
    {
        checker[num] = true ;
    }

    infile.close();

    // print missing numbers

    for (long i = 0; i < SIZE; i++)
    {
        if (!checker[i])
            cout << i << endl ;
    }

    return 0;
}

Komplexität

  • Leerzeichen ~ 232 Bits = 229 Bytes = 219 KB = 29 MB = 1/2 GB
  • Zeit ~ Single Pass
  • Vollständigkeit ~ Ja
2
Khaled.K

Sie müssen sie nicht sortieren, sondern müssen nur Teilmengen von ihnen wiederholt partitionieren.

Der erste Schritt ist wie der erste Durchgang einer Quicksortierung. Wählen Sie eine der Ganzzahlen x aus, und durchlaufen Sie das Array, um alle Werte, die kleiner als x sind, links und Werte, die größer als x sind, rechts davon zu platzieren. Finden Sie heraus, auf welcher Seite von x die meisten Slots verfügbar sind (ganze Zahlen, die nicht in der Liste enthalten sind). Dies kann leicht berechnet werden, indem der Wert von x mit seiner Position verglichen wird. Wiederholen Sie dann die Partition in der Unterliste auf der Seite von x. Wiederholen Sie dann die Partition in der Unter-Unter-Liste mit der größten Anzahl verfügbarer Ganzzahlen usw. Die Gesamtzahl der Vergleiche, um in einen leeren Bereich zu gelangen, sollte ungefähr 4 Milliarden betragen (Geben oder Nehmen).

1
Lucas Membrane

Vielleicht fehlt mir der Punkt dieser Frage vollständig, aber Sie möchten eine Ganzzahl finden, die in einer sortierten -Datei mit Ganzzahlen fehlt?

Ähh ... wirklich? Überlegen wir uns, wie eine solche Datei aussehen würde:

1 2 3 4 5 6 ... erste fehlende Nummer ... usw.

Die Lösung für dieses Problem scheint trivial.

1
hacksoncode

Sie können das Auffinden der fehlenden Ganzzahlen nach dem Lesen der vorhandenen beschleunigen, indem Sie Bereiche nicht besuchter Ganzzahlen in einer Baumstruktur speichern.

Sie beginnen mit dem Speichern von [0..4294967295], und jedes Mal, wenn Sie eine Ganzzahl lesen, verbinden Sie den Bereich, in den sie fällt, und löschen einen Bereich, wenn er leer wird. Am Ende haben Sie den genauen Satz von Ganzzahlen, die in den Bereichen fehlen. Wenn Sie also 5 als erste Ganzzahl sehen, haben Sie [0..4] und [6..4294967295].

Dies ist viel langsamer als das Markieren von Bits, daher wäre dies nur eine Lösung für den 10-MB-Fall, vorausgesetzt, Sie können die unteren Ebenen des Baums in Dateien speichern.

Ein Weg, einen solchen Baum zu speichern, wäre ein B-Baum mit dem Beginn des Bereichs als Schlüssel und dem Ende des Bereichs als Wert. Im schlimmsten Fall werden alle ungeraden oder geraden Zahlen verwendet, was bedeutet, dass 2 ^ 31 Werte oder Dutzende GB für den Baum gespeichert werden ... Autsch. Der beste Fall ist eine sortierte Datei, in der Sie nur einige Ganzzahlen für den gesamten Baum verwenden würden.

Also nicht wirklich die richtige Antwort, aber ich dachte, ich würde diese Vorgehensweise erwähnen. Ich nehme an, ich würde das Interview nicht bestehen ;-)

1
w00t

Alte Frage, aber ich frage mich über die "nicht-funktionalen" Anforderungen. Meiner Meinung nach sollte ein Hinweis gegeben werden - wenn diese Frage an einer anderen Stelle gestellt wurde als in einem Buch, in dem dann alle Möglichkeiten mit Vor- und Nachteilen erörtert werden. Oft genug scheinen es Fragen in Vorstellungsgesprächen zu sein, die mich verwundern, da es keine eindeutige Antwort geben kann, ohne die weichen Anforderungen zu kennen, dh "es muss sehr schnell sein, fehlende Zahlen zu suchen, weil es x-mal in einer Sekunde verwendet wird ".

Ich denke, eine solche Frage könnte eine vernünftige Antwort geben.

  • Ich würde alle Zahlen in eine neue Datei zusammenführen und dabei 4 Byte pro Int. Verwenden. Natürlich wird dies zunächst langsam gehen. Dies kann jedoch mit geringem Speicherplatz geschehen (Sie müssen nicht unbedingt alle im RAM belassen).
  • Verwenden Sie die Binärsuche, um zu überprüfen, ob die Nummer in der vorsortierten Datei vorhanden ist. Da wir 4 Bytes pro Wert bleiben, ist dies kein Problem

nachteile:

  • Dateigröße
  • Langsame erste Sortierung - wird aber nur einmal benötigt

vorteile:

  • sehr schnell zu suchen

Also nochmal eine sehr schöne Frage für ein Buch. Aber ich denke, es ist eine merkwürdige Frage, wenn man nach einer einzigen besten Lösung fragt, wenn das zu lösende Problem nicht vollständig bekannt ist.

0
benjist

Ich lese das vielleicht zu genau, aber in den Fragen steht "generiere eine Ganzzahl, die nicht in der Datei enthalten ist". Ich würde einfach die Liste sortieren und 1 zum maximalen Eintrag hinzufügen. Bam, eine Ganzzahl, die nicht in der Datei enthalten ist.

0
Sib

Ich habe mir den folgenden Algorithmus ausgedacht.

Meine Idee: Die ganze Datei mit ganzen Zahlen einmal durchgehen und für jede Bitposition die Nullen und Einsen zählen. Die Anzahl der Nullen und Einsen muss 2 ^ (numOfBits)/2 sein. Wenn die Anzahl also geringer ist als erwartet, können wir sie von unserer resultierenden Anzahl verwenden.

Angenommen, die Ganzzahl ist 32 Bit, dann benötigen wir

int[] ones = new int[32];
int[] zeroes = new int[32];

Für jede Zahl müssen wir 32 Bits durchlaufen und den Wert von 0 oder 1 erhöhen:

for(int i = 0; i < 32; i++){
   ones[i] += (val>>i&0x1); 
   zeroes[i] += (val>>i&0x1)==1?0:1;
}

Schließlich, nachdem die Datei verarbeitet wurde:

int res = 0;
for(int i = 0; i < 32; i++){
   if(ones[i] < (long)1<<31)res|=1<<i;
}
return res;

HINWEIS: In einigen Sprachen (z. B. Java) ist 1 << 31 eine negative Zahl, daher ist (lang) 1 << 31 der richtige Weg, dies zu tun

0
Timofey