Genera un numero intero che non sia tra i quattro miliardi dati


692

Mi è stata data questa domanda di intervista:

Dato un file di input con quattro miliardi di numeri interi, fornire un algoritmo per generare un numero intero non contenuto nel file. Supponiamo di avere 1 GB di memoria. Segui cosa faresti se avessi solo 10 MB di memoria.

La mia analisi:

La dimensione del file è 4 × 10 9 × 4 byte = 16 GB.

Possiamo fare l'ordinamento esterno, facendoci così conoscere l'intervallo degli interi.

La mia domanda è: qual è il modo migliore per rilevare il numero intero mancante nei set di numeri interi grandi ordinati?

La mia comprensione (dopo aver letto tutte le risposte):

Supponendo che stiamo parlando di interi a 32 bit, ci sono 2 32 = 4 * 10 9 interi distinti.

Caso 1: abbiamo 1 GB = 1 * 10 9 * 8 bit = 8 miliardi di memoria.

Soluzione:

Se usiamo un bit che rappresenta un intero distinto, è sufficiente. non abbiamo bisogno dell'ordinamento.

Implementazione:

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);
        }
    }
}

Caso 2: 10 MB di memoria = 10 * 10 6 * 8 bit = 80 milioni di bit

Soluzione:

Per tutti i possibili prefissi a 16 bit, ci sono 2 16 numero di numeri interi = 65536, abbiamo bisogno di 2 16 * 4 * 8 = 2 milioni di bit. Abbiamo bisogno di costruire 65536 secchi. Per ogni bucket, abbiamo bisogno di 4 byte con tutte le possibilità perché nel caso peggiore tutti i 4 miliardi di numeri interi appartengono allo stesso bucket.

  1. Costruisci il contatore di ogni bucket attraverso il primo passaggio del file.
  2. Scansiona i secchi, trova il primo con meno di 65536 colpi.
  3. Costruisci nuovi bucket i cui alti prefissi a 16 bit sono stati trovati nel passaggio 2 attraverso il secondo passaggio del file
  4. Analizza i bucket incorporati nel passaggio 3, trova il primo bucket che non ha un successo.

Il codice è molto simile a quello sopra.

Conclusione: riduciamo la memoria aumentando il passaggio dei file.


Un chiarimento per chi arriva in ritardo: la domanda, come posta, non dice che esiste esattamente un numero intero che non è contenuto nel file, almeno non è così che la maggior parte delle persone lo interpreta. Molti commenti nel commento filo sono circa che la variazione del compito, però. Sfortunatamente il commento che lo ha introdotto nel thread dei commenti è stato successivamente eliminato dal suo autore, quindi ora sembra che le risposte orfane ad esso abbiano frainteso tutto. È molto confuso, scusa.


32
@trashgod, sbagliato. Per 4294967295 numeri interi unici avrai 1 numero intero rimanente. Per trovarlo, dovresti sommare tutti i numeri interi e sottrarlo dalla somma precalcolata di tutti i possibili numeri interi.
Nakilon,

58
Questa è la seconda "perla" di "Programmazione di perle", e suggerirei di leggere l'intera discussione nel libro. Vedi books.google.com/…
Alok Singhal,

8
@Richard a 64 bit int sarebbe più che abbastanza grande.
cftarnas,

79
int getMissingNumber(File inputFile) { return 4; }( riferimento )
johnny il

14
Non importa che non sia possibile memorizzare la somma di tutti i numeri interi da 1 a 2 ^ 32 perché il tipo intero in linguaggi come C / C ++ SEMPRE conserva proprietà come associatività e comunicatività. Ciò significa che, sebbene la somma non sia la risposta giusta, se si calcola il previsto con overflow, la somma effettiva con overflow e quindi si sottrae, il risultato sarà comunque corretto (purché esso stesso non trabocchi).
thedayturns

Risposte:


530

Supponendo che "numero intero" significhi 32 bit : 10 MB di spazio sono più che sufficienti per contare quanti numeri ci sono nel file di input con un dato prefisso a 16 bit, per tutti i possibili prefissi a 16 bit in un passaggio attraverso il file di input. Almeno uno dei secchi sarà colpito meno di 2 16 volte. Fai un secondo passaggio per scoprire quali dei possibili numeri in quel bucket sono già utilizzati.

Se significa più di 32 bit, ma comunque di dimensioni limitate : fare come sopra, ignorando tutti i numeri di input che non rientrano nell'intervallo (con o senza segno; a scelta) a 32 bit.

Se "numero intero" significa numero intero matematico : leggi una volta l'input e tieni traccia della lunghezza del numero più grande del numero più lungo che tu abbia mai visto. Al termine, emetti il massimo più uno un numero casuale che ha un'altra cifra. (Uno dei numeri nel file può essere un bignum che richiede più di 10 MB per rappresentare esattamente, ma se l'input è un file, puoi almeno rappresentare la lunghezza di tutto ciò che si adatta in esso).


24
Perfetto. La tua prima risposta richiede solo 2 passaggi nel file!
corsiKa

47
Un bignum da 10 MB? È piuttosto estremo.
Mark Ransom,

12
@Legate, salta semplicemente i numeri ingranditi e non fare nulla al riguardo. Dal momento che non hai intenzione di emettere un numero ingrandito comunque, non è necessario tenere traccia di quale di essi hai visto.
Hmakholm ha lasciato Monica il

12
L'aspetto positivo della soluzione 1 è che è possibile ridurre la memoria aumentando i passaggi.
Yousf,

11
@ Barry: la domanda sopra non indica che manca esattamente un numero. Non dice nemmeno che i numeri nel file non si ripetano. (Seguire la domanda effettivamente posta è probabilmente una buona idea in un'intervista, giusto? ;-))
Christopher Creutzig

197

Gli algoritmi statisticamente informati risolvono questo problema utilizzando meno passaggi rispetto agli approcci deterministici.

Se sono ammessi numeri interi molto grandi, è possibile generare un numero che sarà probabilmente unico nel tempo O (1). Un intero pseudo-casuale a 128 bit come un GUID si scontrerà con uno dei quattro miliardi di interi esistenti nel set in meno di uno su 64 miliardi di miliardi di casi.

Se i numeri interi sono limitati a 32 bit, è possibile generare un numero che sarà probabilmente univoco in un singolo passaggio utilizzando molto meno di 10 MB. La probabilità che un intero pseudo-casuale a 32 bit entrerà in collisione con uno dei 4 miliardi di numeri interi esistenti è di circa il 93% (4e9 / 2 ^ 32). Le probabilità che 1000 interi pseudo-casuali si scontrino tutti sono inferiori a una su 12.000 miliardi di miliardi di miliardi (probabilità di una collisione ^ 1000). Quindi, se un programma mantiene una struttura di dati contenente 1000 candidati pseudo-casuali e scorre attraverso gli interi noti, eliminando le corrispondenze dai candidati, è quasi certo di trovare almeno un numero intero che non si trova nel file.


32
Sono abbastanza sicuro che gli interi siano limitati. Se non lo fossero, allora anche un programmatore principiante penserebbe all'algoritmo "fai un passaggio tra i dati per trovare il numero massimo e aggiungi 1"
Adrian Petrescu,

12
Indovinare letteralmente un risultato casuale probabilmente non ti farà guadagnare molti punti in un'intervista
Brian Gordon,

6
@Adrian, la tua soluzione sembra ovvia (ed è stato per me, l'ho usato nella mia risposta) ma non è ovvio per tutti. È un buon test per vedere se riesci a individuare soluzioni ovvie o se complicherai eccessivamente tutto ciò che tocchi.
Mark Ransom,

19
@Brian: penso che questa soluzione sia allo stesso tempo fantasiosa e pratica. Io per primo darei molti complimenti per questa risposta.
Richard H,

6
ah qui sta il confine tra ingegneri e scienziati. Ottima risposta Ben!
TrojanName

142

Una discussione dettagliata su questo problema è stata discussa in Jon Bentley "Colonna 1. Cracking the Oyster" Programmazione Pearls Addison-Wesley pp.3-10

Bentley discute di diversi approcci, incluso l'ordinamento esterno, Unisci ordinamento usando diversi file esterni ecc., Ma il metodo migliore che Bentley suggerisce è un algoritmo a passaggio singolo che utilizza campi di bit , che chiama umoristicamente "Wonder Sort" :) Venendo al problema, 4 miliardi i numeri possono essere rappresentati in:

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

Il codice per implementare il bitset è semplice: (preso dalla pagina delle soluzioni )

#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)); }

L'algoritmo di Bentley esegue un singolo passaggio sul file, setinserendo il bit appropriato nell'array e quindi esamina questo array utilizzando la testmacro sopra per trovare il numero mancante.

Se la memoria disponibile è inferiore a 0,466 GB, Bentley suggerisce un algoritmo k-pass, che divide l'input in intervalli a seconda della memoria disponibile. Per fare un esempio molto semplice, se fosse disponibile solo 1 byte (ovvero memoria per gestire 8 numeri) e l'intervallo fosse compreso tra 0 e 31, lo dividiamo in intervalli da 0 a 7, 8-15, 16-22 e così via e gestire questo intervallo in ciascuno dei 32/8 = 4passaggi.

HTH.


12
Non conosco il libro, ma nessun motivo per chiamarlo "Wonder Sort", in quanto è solo un secchio, con un contatore a 1 bit.
flolo,

3
Sebbene sia più portatile, questo codice verrà annullato dal codice scritto per utilizzare istruzioni vettoriali supportate dall'hardware . Penso che in alcuni casi gcc possa convertire automaticamente il codice in operazioni vettoriali.
Brian Gordon,

3
@brian Non penso che Jon Bentley stia permettendo queste cose nel suo libro sugli algoritmi.
David Heffernan,

8
@BrianGordon, il tempo trascorso in ram sarà trascurabile rispetto al tempo trascorso a leggere il file. Dimentica l'ottimizzazione.
Ian,

1
@BrianGordon: O stavi parlando del loop alla fine per trovare il primo bit non impostato? Sì, i vettori lo accelereranno, ma != -1eseguendo il looping del bitfield con numeri interi a 64 bit, cercando uno che possa ancora saturare la larghezza di banda della memoria in esecuzione su un singolo core (questo è SIMD-in-a-register, SWAR, con bit come elementi). (Per i recenti progetti Intel / AMD). Devi solo capire quale bit non è impostato dopo aver trovato la posizione a 64 bit che lo contiene. (E per quello che puoi not / lzcnt.) Il punto giusto che il loop su un test a bit singolo potrebbe non essere ottimizzato bene.
Peter Cordes,

120

Poiché il problema non specifica che dobbiamo trovare il numero più piccolo possibile che non si trova nel file, potremmo semplicemente generare un numero più lungo del file di input stesso. :)


6
A meno che il numero più grande nel file sia max int, allora ti
limiterai

Quale sarebbe la dimensione di quel file in un programma del mondo reale che potrebbe essere necessario generare un nuovo numero intero e accodarlo al file "numeri interi usati" 100 volte?
Michael,

2
Ci stavo pensando. Supponendo che intsiano 32bit, solo output 2^64-1. Fatto.
imallett,

1
Se è un int per riga tr -d '\n' < nums.txt > new_num.txt:: D
Shon

56

Per la variante da 1 GB RAM è possibile utilizzare un bit vettore. È necessario allocare 4 miliardi di bit == 500 MB di array di byte. Per ogni numero letto dall'ingresso, impostare il bit corrispondente su "1". Una volta fatto, scorrere i bit, trovare il primo che è ancora '0'. Il suo indice è la risposta.


4
L'intervallo di numeri nell'input non è specificato. Come funziona questo algoritmo se l'input è composto da tutti i numeri pari tra 8 e 16 miliardi?
Mark Ransom,

27
@Mark, ignora gli input che non rientrano nell'intervallo 0..2 ^ 32. Non ne uscirai comunque nessuno, quindi non è necessario ricordare quale di essi evitare.
Hmakholm ha lasciato Monica il

@Mark qualsiasi algoritmo che usi per determinare come una stringa a 32 bit si associa a un numero reale dipende da te. Il processo è sempre lo stesso. L'unica differenza è come stamparlo come numero reale sullo schermo.
corsiKa

4
Invece di iterare te stesso puoi usare bitSet.nextClearBit(0): download.oracle.com/javase/6/docs/api/java/util/…
starblue,

3
Sarebbe utile menzionare che, indipendentemente dall'intervallo degli interi, almeno un bit è garantito per essere 0 alla fine del passaggio. Ciò è dovuto al principio del buco del piccione.
Rafał Dowgird,

46

Se sono numeri interi a 32 bit (probabilmente dalla scelta di ~ 4 miliardi di numeri vicini a 2 32 ), l'elenco di 4 miliardi di numeri occuperà al massimo il 93% dei possibili numeri interi (4 * 10 9 / (2 32 ) ). Quindi, se crei un array di bit di 2 32 bit con ogni bit inizializzato su zero (che occuperà 2 29 byte ~ 500 MB di RAM; ricorda un byte = 2 3 bit = 8 bit), leggi l'elenco di numeri interi e per ogni int impostare l'elemento bit-array corrispondente da 0 a 1; e poi leggi il tuo bit-array e restituisci il primo bit che è ancora 0.

Nel caso in cui si disponga di meno RAM (~ 10 MB), questa soluzione deve essere leggermente modificata. 10 MB ~ 83886080 bit sono ancora sufficienti per eseguire un array di bit per tutti i numeri compresi tra 0 e 83886079. In questo modo è possibile leggere l'elenco di ints; e registra solo # tra 0 e 83886079 nel tuo array di bit. Se i numeri sono distribuiti casualmente; con una probabilità schiacciante (differisce del 100% da circa 10 -2592069 ) troverai un int mancante). Infatti, se scegli solo i numeri da 1 a 2048 (con solo 256 byte di RAM) troverai comunque un numero mancante in una percentuale schiacciante (99.99999999999999999999999999999999999999999999999999999999999999999595%) del tempo.

Ma diciamo invece di avere circa 4 miliardi di numeri; avevi qualcosa come 2 32 - 1 numeri e meno di 10 MB di RAM; quindi ogni piccolo intervallo di ints ha solo una piccola possibilità di non contenere il numero.

Se ti fosse garantito che ogni int nell'elenco è univoco, puoi sommare i numeri e sottrarre la somma con un # mancante alla somma completa (½) (2 32 ) (2 32 - 1) = 9223372034707292160 per trovare l'int mancante . Tuttavia, se un int si è verificato due volte, questo metodo fallirà.

Tuttavia, puoi sempre dividere e conquistare. Un metodo ingenuo sarebbe leggere l'array e contare il numero di numeri presenti nella prima metà (da 0 a 2 31 -1) e nella seconda metà (2 31 , 2 32 ). Quindi seleziona l'intervallo con meno numeri e ripeti dividendo l'intervallo a metà. (Di 'se ci fossero due numeri in meno in (2 31 , 2 32 ), la tua prossima ricerca avrebbe conteggiato i numeri nell'intervallo (2 31 , 3 * 2 30 -1), (3 * 2 30 , 2 32 ). ripetendo fino a quando non trovi un intervallo con zero numeri e hai la risposta. Dovresti prendere O (lg N) ~ 32 letture attraverso l'array.

Quel metodo era inefficiente. Stiamo usando solo due numeri interi in ogni passaggio (o circa 8 byte di RAM con un numero intero di 4 byte (32 bit)). Un metodo migliore sarebbe quello di dividere in sqrt (2 32 ) = 2 16 = 65536 bin, ciascuno con 65536 numeri in un cestino. Ogni bin richiede 4 byte per memorizzare il suo conteggio, quindi sono necessari 2 18 byte = 256 kB. Quindi il contenitore 0 è (da 0 a 65535 = 2 16 -1), il contenitore 1 è (2 16 = da 65536 a 2 * 2 16 -1 = 131071), il contenitore 2 è (2 * 2 16 = da 131072 a 3 * 2 16 - 1 = 196607). In Python avresti qualcosa del tipo:

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)

Leggi l'elenco di ~ 4 miliardi di numeri interi; e conta quanti ints cadono in ciascuno dei 2 16 bin e trova un incomplete_bin che non ha tutti i 65536 numeri. Quindi leggi di nuovo l'elenco di 4 miliardi di numeri interi; ma questa volta notate solo quando gli interi sono in quell'intervallo; capovolgendoli quando li trovi.

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

3
Una risposta così fantastica. Questo effettivamente funzionerebbe; e ha risultati garantiti.
Jonathan Dickinson,

@dr jimbob, cosa succede se c'è un solo numero in un cestino e quel singolo numero ha 65535 duplicati? In tal caso, il cestino conterà ancora 65536, ma tutti i numeri 65536 sono uguali.
Alcott,

@Alcott - Supponevo che avessi 2 ^ 32-1 (o meno) numeri, quindi per il principio del buco del piccione sei sicuro di avere un cestino con meno di 65536 conteggi per controllare più in dettaglio. Stiamo cercando di trovare solo un intero mancante, non tutti. Se avessi 2 ^ 32 o più numeri, non puoi garantire un numero intero mancante e non saresti in grado di utilizzare questo metodo (o avere una garanzia sin dall'inizio c'è un numero intero mancante). La tua scommessa migliore sarebbe quindi la forza bruta (ad esempio, leggi l'array 32 volte; controlla i primi 65536 #s la prima volta; e fermati quando viene trovata una risposta).
dr jimbob,

Il metodo intelligente superiore 16 / inferiore 16 è stato pubblicato in precedenza da Henning: stackoverflow.com/a/7153822/224132 . Tuttavia, ho adorato l'idea del componente aggiuntivo per un set unico di numeri interi che mancano esattamente a un membro.
Peter Cordes,

3
@PeterCordes - Sì, la soluzione di Henning precede la mia, ma penso che la mia risposta sia ancora utile (esaminando diverse cose in modo più dettagliato). Detto questo, Jon Bentley nel suo libro Programming Pearls ha suggerito un'opzione multi-pass per questo problema (vedi la risposta di Vine'th) molto prima che esistesse lo stackoverflow (non che sto sostenendo che uno di noi ha rubato consapevolmente da lì o che Bentley è stato il primo a analizzare questo problema - è una soluzione abbastanza naturale da sviluppare). Due passaggi sembrano più naturali quando il limite è che non si ha più memoria sufficiente per una soluzione a 1 passaggio con un array di bit giganti.
dr jimbob,

37

Perché renderlo così complicato? Richiedi un numero intero non presente nel file?

Secondo le regole specificate, l'unica cosa che devi archiviare è il numero intero più grande che hai incontrato finora nel file. Dopo aver letto l'intero file, restituire un numero 1 maggiore di quello.

Non vi è alcun rischio di colpire maxint o altro, perché secondo le regole, non vi è alcuna restrizione alla dimensione dell'intero o al numero restituito dall'algoritmo.


4
Funzionerebbe a meno che il massimo int non fosse nel file, il che è del tutto possibile ...
PearsonArtPhoto

13
Le regole non specificano che è 32 bit o 64 bit o altro, quindi in base alle regole specificate, non esiste un valore int max. Intero non è un termine informatico, è un termine matematico che identifica numeri interi positivi o negativi.
Pete,

Abbastanza vero, ma non si può presumere che sia un numero a 64 bit o che qualcuno non si limiterebbe a intrufolarsi nel numero max int solo per confondere tali algoritmi.
PearsonArtPhoto,

24
L'intera nozione di "max int" non è valida nel contesto se non è stato specificato alcun linguaggio di programmazione. ad es. guardare la definizione di Python di un intero lungo. È illimitato. Non c'è tetto. Puoi sempre aggiungerne uno. Supponete che sia implementato in una lingua che ha un valore massimo consentito per un numero intero.
Pete,

32

Questo può essere risolto in pochissimo spazio usando una variante della ricerca binaria.

  1. Inizia con l'intervallo di numeri consentito 0a 4294967295.

  2. Calcola il punto medio.

  3. Scorrere il file, contando quanti numeri erano uguali, inferiori o superiori al valore del punto medio.

  4. Se nessun numero era uguale, il gioco è fatto. Il numero del punto medio è la risposta.

  5. Altrimenti, scegli l'intervallo con il minor numero di numeri e ripeti dal passaggio 2 con questo nuovo intervallo.

Ciò richiederà fino a 32 scansioni lineari attraverso il file, ma utilizzerà solo pochi byte di memoria per memorizzare l'intervallo e i conteggi.

Questo è essenzialmente lo stesso della soluzione di Henning , tranne per il fatto che utilizza due contenitori anziché 16k.


2
È quello con cui ho iniziato, prima di iniziare l'ottimizzazione per i parametri indicati.
Hmakholm ha lasciato Monica il

@Henning: Cool. È un bell'esempio di algoritmo in cui è facile modificare il compromesso spazio-temporale.
Hammar,

@hammar, ma cosa succede se ci sono quei numeri che compaiono più di una volta?
Alcott,

@Alcott: quindi l'algoritmo sceglierà il cestino più denso invece del cestino più sparso, ma per il principio del buco del piccione, non potrà mai scegliere un cestino completamente pieno. (Il più piccolo dei due conteggi sarà sempre inferiore all'intervallo del cestino).
Peter Cordes,

27

EDIT Ok, questo non è stato del tutto considerato in quanto presuppone che gli interi nel file seguano una distribuzione statica. Apparentemente non è necessario, ma anche allora si dovrebbe provare questo:


Esistono billion4,3 miliardi di numeri interi a 32 bit. Non sappiamo come siano distribuiti nel file, ma il caso peggiore è quello con la più alta entropia di Shannon: una distribuzione equa. In questo caso, è probabile che non si verifichi un numero intero nel file

((2³²-1) / 2³²) ⁴ ⁰⁰⁰ ⁰⁰⁰ ⁰⁰⁰ ≈ .4

Più bassa è l'entropia di Shannon, maggiore è questa probabilità mediamente, ma anche nel caso peggiore abbiamo una probabilità del 90% di trovare un numero non ricorrente dopo 5 ipotesi con numeri interi casuali. Basta creare tali numeri con un generatore pseudocasuale, memorizzarli in un elenco. Quindi leggi int dopo int e confrontalo con tutte le tue ipotesi. In caso di corrispondenza, rimuovere questa voce dell'elenco. Dopo aver esaminato tutto il file, è probabile che rimarrà più di un'ipotesi. Usa uno di loro. Nel raro (10% anche nel peggiore dei casi) caso in cui non si indovina, ottenere un nuovo set di numeri interi casuali, forse più questa volta (10-> 99%).

Consumo di memoria: poche decine di byte, complessità: O (n), sovraccarico: nimibile poiché la maggior parte del tempo sarà speso negli inevitabili accessi al disco rigido piuttosto che confrontare ints comunque.


Il caso peggiore effettivo, quando non assumiamo una distribuzione statica, è che ogni numero intero si verifichi max. una volta, perché solo 1 - 4000000000 / 2³² ≈ il 6% di tutti i numeri interi non compare nel file. Quindi avrai bisogno di qualche altra ipotesi, ma ciò non costerà comunque quantità di memoria dannose.


5
Sono contento di vedere qualcun altro pensato a questo, ma perché è qui in fondo? Questo è un algoritmo a 1 passaggio ... 10 MB sono sufficienti per 2,5 M di ipotesi e il 93% ^ 2,5 M ≈ 10 ^ -79000 è davvero una possibilità trascurabile di aver bisogno di una seconda scansione. A causa del sovraccarico della ricerca binaria, va più veloce se usi meno ipotesi! Questo è ottimale sia nel tempo che nello spazio.
Potatoswatter,

1
@Potatoswatter: bene hai citato la ricerca binaria. Questo probabilmente non vale il sovraccarico quando si usano solo 5 ipotesi, ma è certamente a 10 o più. Potresti anche fare le ipotesi 2 M, ma poi dovresti memorizzarle in un set di hash per ottenere O (1) per la ricerca.
lasciato circa il

1
@Potatoswatter La risposta equivalente di Ben Haley è vicina alla cima
Brian Gordon

1
Mi piace questo approccio, ma suggerirei un miglioramento del risparmio di memoria: se uno ha N bit di memoria indicizzata disponibile, oltre a una memoria costante, definire una funzione di rimescolamento configurabile reversibile a 32 bit (permutazione), selezionare una permutazione arbitraria e cancellare tutto bit indicizzati. Quindi leggi ogni numero dal file, mescolalo e, se il risultato è inferiore a N, imposta il bit corrispondente. Se un bit non è impostato alla fine del file, invertire la funzione di scramble sul suo indice. Con 64 KB di memoria, si potrebbe effettivamente testare la disponibilità di oltre 512.000 numeri in un unico passaggio.
supercat

2
Naturalmente, con questo algoritmo, il caso peggiore è quello in cui i numeri sono stati creati dallo stesso generatore di numeri casuali che si sta utilizzando. Supponendo che tu possa garantire che non è così, la tua tattica migliore è quella di utilizzare un generatore di numeri casuali congruenziali e lineari per generare la tua lista, in modo da attraversare lo spazio numerico in modo pseudocasuale. Ciò significa che se in qualche modo fallisci, puoi continuare a generare numeri fino a quando non hai coperto l'intera gamma di ints (di aver trovato un gap), senza mai duplicare i tuoi sforzi.
Dewi Morgan,

25

Se manca un numero intero compreso nell'intervallo [0, 2 ^ x - 1], quindi semplicemente Xor tutti insieme. Per esempio:

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

(So ​​che questo non risponde esattamente alla domanda , ma è una buona risposta a una domanda molto simile.)


1
Sì, è facile provare [ ] che funziona quando manca un numero intero, ma spesso fallisce se ne manca più di uno. Ad esempio, 0 ^ 1 ^ 3 ^ 4 ^ 6 ^ 7è 0. [ Scrivendo 2 x per 2 a x'th potenza e a ^ b per a xor b, il xor di tutto k <2 x è zero - k ^ ~ k = (2 ^ x) - 1 per k <2 ^ (x-1) e k ^ ~ k ^ j ^ ~ j = 0 quando j = k + 2 ** (x-2) - quindi lo xor di tutti tranne un numero è il valore di quello mancante]
James Waldby - jwpat7,

2
Come menziono in un commento sulla risposta di ircmaxell: il problema non dice "manca un numero", dice di trovare un numero non incluso nei 4 miliardi di numeri nel file. Se assumiamo numeri interi a 32 bit, nel file potrebbero mancare circa 300 milioni di numeri. La probabilità che lo xor dei numeri presenti corrisponda a un numero mancante è solo del 7% circa.
James Waldby - jwpat7,

Questa è la risposta che stavo pensando quando ho letto inizialmente la domanda, ma a un esame più attento penso che la domanda sia più ambigua di questa. Cordiali saluti, questa è la domanda a cui stavo pensando: stackoverflow.com/questions/35185/…
Lee Netherton

18

Potrebbero cercare di vedere se hai sentito parlare di un filtro Bloom probabilistico che può determinare in modo molto efficiente assolutamente se un valore non fa parte di un set di grandi dimensioni (ma può determinare solo con alta probabilità che è un membro dell'insieme.)


4
Con probabilmente oltre il 90% dei possibili valori impostati, il tuo Bloom Filter dovrebbe probabilmente degenerare nel campo di bit che molte risposte già utilizzano. Altrimenti, finirai con un inutile bittring completamente riempito.
Christopher Creutzig,

@Christopher La mia comprensione dei filtri Bloom è che non ottieni un bitarray pieno fino a quando non raggiungi il 100%
Paul

... altrimenti otterresti falsi negativi.
Paul,

@Paul un array di bit riempito ti dà falsi positivi, che sono ammessi. In questo caso il filtro di fioritura degenererebbe molto probabilmente nel caso in cui la soluzione, che sarebbe negativa, restituisca un falso positivo.
ataylor,

1
@Paul: puoi ottenere un bitarray riempito non appena il numero di funzioni hash moltiplicato per il numero di voci è grande quanto la lunghezza del tuo campo. Certo, sarebbe un caso eccezionale, ma la probabilità aumenterà abbastanza rapidamente.
Christopher Creutzig,

17

Sulla base dell'attuale formulazione della domanda originale, la soluzione più semplice è:

Trova il valore massimo nel file, quindi aggiungi 1 ad esso.


5
Cosa succede se MAXINT è incluso nel file?
Petr Peller,

@Petr Peller: una libreria BIGINT rimuove essenzialmente le limitazioni sulla dimensione dei numeri interi.
oosterwal,

2
@oosterwal, se questa risposta fosse consentita, non è nemmeno necessario leggere il file: basta stampare il maggior numero possibile.
Nakilon,

1
@oosterwal, se il tuo numero enorme casuale era il più grande che tu potessi stampare, ed era in archivio, allora questo compito non poteva essere risolto.
Nakilon,

3
@Nakilon: +1 Il tuo punto è preso. È all'incirca equivalente a calcolare il numero totale di cifre nel file e stampare un numero con così tante cifre.
Oosterwal,

14

Usa a BitSet. 4 miliardi di numeri interi (assumendo fino a 2 ^ 32 numeri interi) impacchettati in un BitSet a 8 per byte è 2 ^ 32/2 ^ 3 = 2 ^ 29 = circa 0,5 Gb.

Per aggiungere un po 'più di dettaglio - ogni volta che leggi un numero, imposta il bit corrispondente nel BitSet. Quindi, fai un passaggio sul BitSet per trovare il primo numero che non è presente. In effetti, potresti farlo altrettanto efficacemente scegliendo ripetutamente un numero casuale e testando se è presente.

In realtà BitSet.nextClearBit (0) ti dirà il primo bit non impostato.

Guardando l'API BitSet, sembra supportare solo 0..MAX_INT, quindi potresti aver bisogno di 2 BitSet - uno per + 've numeri e uno per-have numeri - ma i requisiti di memoria non cambiano.


1
O se non vuoi usare un BitSet... prova una serie di bit. Fa la stessa cosa;)
jcolebrand,

12

Se non esiste un limite di dimensioni, il modo più rapido è quello di prendere la lunghezza del file e generare la lunghezza del file + 1 numero di cifre casuali (o solo "11111 ..."). Vantaggio: non è nemmeno necessario leggere il file e è possibile ridurre al minimo l'utilizzo della memoria quasi a zero. Svantaggio: verranno stampati miliardi di cifre.

Tuttavia, se l'unico fattore fosse ridurre al minimo l'utilizzo della memoria, e nient'altro è importante, questa sarebbe la soluzione ottimale. Potrebbe anche farti ottenere il premio "peggior abuso delle regole".


11

Se assumiamo che l'intervallo di numeri sarà sempre 2 ^ n (una potenza pari a 2), allora esclusivo o funzionerà (come mostrato da un altro poster). Per quanto riguarda il perché, dimostriamolo:

La teoria

Dato qualsiasi intervallo di numeri interi basato su 0 che ha 2^nelementi con un elemento mancante, puoi trovare quell'elemento mancante semplicemente xorando i valori noti insieme per produrre il numero mancante.

La prova

Diamo un'occhiata a n = 2. Per n = 2, possiamo rappresentare 4 numeri interi univoci: 0, 1, 2, 3. Hanno un modello di bit di:

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

Ora, se guardiamo, ogni singolo bit è impostato esattamente due volte. Pertanto, poiché è impostato un numero pari di volte e l'esclusiva o dei numeri produrrà 0. Se manca un singolo numero, l'esclusiva o produrrà un numero che, se eseguito in esclusiva con il numero mancante, comporterà 0. Pertanto, il numero mancante e il numero risultante in ordine esclusivo sono esattamente gli stessi. Se rimuoviamo 2, lo xor risultante sarà 10(o 2).

Ora diamo un'occhiata a n + 1. Chiamiamo il numero di volte in cui ogni bit occupa n, xe il numero di volte in cui ogni bit è impostato in n+1 y. Il valore di ysarà uguale a y = x * 2perché ci sono xelementi con il n+1bit impostato su 0 e xelementi con il n+1bit impostato su 1. E poiché 2xsarà sempre pari, n+1ogni bit verrà sempre impostato un numero pari di volte.

Pertanto, poiché n=2funziona e n+1funziona, il metodo xor funzionerà per tutti i valori di n>=2.

L'algoritmo per 0 intervalli basati

Questo è abbastanza semplice Utilizza 2 * n bit di memoria, quindi per qualsiasi intervallo <= 32, 2 interi a 32 bit funzioneranno (ignorando qualsiasi memoria consumata dal descrittore di file). E fa un singolo passaggio del file.

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

L'algoritmo per gli intervalli arbitrari

Questo algoritmo funzionerà per intervalli di qualsiasi numero iniziale a qualsiasi numero finale, a condizione che l'intervallo totale sia uguale a 2 ^ n ... Questo fondamentalmente ridisegna l'intervallo per avere il minimo a 0. Ma richiede 2 passaggi attraverso il file (il primo a prendere il minimo, il secondo a calcolare l'int mancante).

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;

Arbitrary Ranges

Possiamo applicare questo metodo modificato a un insieme di intervalli arbitrari, poiché tutti gli intervalli attraverseranno una potenza di 2 ^ n almeno una volta. Funziona solo se c'è un singolo bit mancante. Richiede 2 passaggi di un file non ordinato, ma troverà ogni volta il singolo numero mancante:

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;

Fondamentalmente, ricondiziona l'intervallo intorno a 0. Quindi, conta il numero di valori non ordinati da aggiungere mentre calcola l'esclusivo o. Quindi, aggiunge 1 al conteggio dei valori non ordinati per occuparsi del valore mancante (contare quello mancante). Quindi, mantenere il valore di xoring del valore n, incrementato di 1 ogni volta fino a quando n è una potenza di 2. Il risultato viene quindi ricondizionato alla base originale. Fatto.

Ecco l'algoritmo che ho testato in PHP (usando un array anziché un file, ma lo stesso concetto):

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;
}

Alimentato in un array con qualsiasi intervallo di valori (ho testato inclusi i negativi) con uno all'interno di quell'intervallo mancante, ha trovato il valore corretto ogni volta.

Un altro approccio

Dal momento che possiamo utilizzare l'ordinamento esterno, perché non controllare solo un gap? Se assumiamo che il file sia ordinato prima dell'esecuzione di questo algoritmo:

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;

3
Il problema non dice "manca un numero", dice di trovare un numero non incluso nei 4 miliardi di numeri nel file. Se assumiamo numeri interi a 32 bit, nel file potrebbero mancare circa 300 milioni di numeri. La probabilità che lo xor dei numeri presenti corrisponda a un numero mancante è solo del 7% circa.
James Waldby - jwpat7,

Se hai un intervallo contiguo ma mancante che non è basato su zero, aggiungi invece di xor. sum(0..n) = n*(n+1)/2. Così missing = nmax*(nmax+1)/2 - nmin*(nmin+1)/2 - sum(input[]). (somma idea dalla risposta di @hammar.)
Peter Cordes,

9

Domanda ingannevole, a meno che non sia stato citato in modo improprio. Basta leggere il file una volta per ottenere il numero intero massimo ne tornare n+1.

Naturalmente avresti bisogno di un piano di backup nel caso in cui si verifichi n+1un overflow di numeri interi.


3
Ecco una soluzione che funziona ... tranne quando non lo fa. Utile! :-)
dty

A meno che non sia stato citato in modo improprio, la domanda non ha posto un limite al tipo di numero intero, né al linguaggio utilizzato. Molte lingue moderne hanno numeri interi limitati solo dalla memoria disponibile. Se il numero intero più grande nel file è> 10 MB, sfortuna, compito impossibile per il secondo caso. La mia soluzione preferita
Jürgen Strobel,

9

Controlla la dimensione del file di input, quindi emetti un numero troppo grande per essere rappresentato da un file di quelle dimensioni. Questo può sembrare un trucco economico, ma è una soluzione creativa a un problema di intervista, elimina chiaramente il problema di memoria ed è tecnicamente 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);
    }
}

Dovrebbe stampare 10 bitcount - 1 , che sarà sempre maggiore di 2 bitcount . Tecnicamente, il numero che devi battere è 2 bitcount - (4 * 10 9 - 1) , poiché sai che ci sono (4 miliardi - 1) altri numeri interi nel file e anche con una compressione perfetta occuperanno almeno un po 'ciascuno.


Perché non solo al Console.Write( 1 << bitcount )posto del ciclo? Se ci sono n bit nel file, allora qualsiasi numero di bit (_n_ + 1) con un 1 iniziale è assolutamente garantito per essere più grande.
Emmet

@Emmet - Ciò provocherebbe solo un overflow dei numeri interi, a meno che il file non fosse più piccolo della dimensione di un int (4 byte in C #). C ++ potrebbe consentirti di utilizzare qualcosa di più grande, ma C # non sembra consentire nient'altro che 32 bit con l' <<operatore. In entrambi i casi, a meno che non si arrotoli il proprio gigantesco tipo intero, avrà dimensioni di file molto ridotte. Demo: rextester.com/BLETJ59067
Justin Morgan,

8
  • L'approccio più semplice è trovare il numero minimo nel file e restituire 1 in meno. Questo utilizza la memoria O (1) e il tempo O (n) per un file di n numeri. Tuttavia, fallirà se l'intervallo di numeri è limitato, il che potrebbe rendere min-1 non un numero.

  • Il metodo semplice e diretto di utilizzare una bitmap è già stato menzionato. Quel metodo usa O (n) tempo e memoria.

  • È stato anche menzionato un metodo a 2 passaggi con 2 ^ 16 bucket di conteggio. Legge numeri interi 2 * n, quindi utilizza O (n) time e O (1) storage, ma non può gestire set di dati con più di 2 ^ 16 numeri. Tuttavia, è facilmente esteso a (ad esempio) 2 ^ 60 numeri interi a 64 bit eseguendo 4 passaggi anziché 2, e facilmente adattato all'utilizzo della memoria minuscola utilizzando solo il numero di bin che si adatta alla memoria e aumentando il numero di passaggi corrispondentemente, in in questo caso il tempo di esecuzione non è più O (n) ma è invece O (n * log n).

  • Il metodo di XOR di riunire tutti i numeri, menzionato finora da rfrankel e infine da ircmaxell, risponde alla domanda posta nello stackoverflow n. 35185 , come ha sottolineato ltn100. Utilizza la memoria O (1) e il tempo di esecuzione O (n). Se per il momento ipotizziamo numeri interi a 32 bit, XOR ha una probabilità del 7% di produrre un numero distinto. Razionale: dati ~ 4G numeri distinti XOR'd insieme e ca. 300M non nel file, il numero di bit impostati in ogni posizione di bit ha pari probabilità di essere pari o dispari. Pertanto, 2 ^ 32 numeri hanno la stessa probabilità di sorgere come risultato XOR, di cui il 93% è già in archivio. Si noti che se i numeri nel file non sono tutti distinti, la probabilità di successo del metodo XOR aumenta.


7

Per qualche motivo, non appena ho letto questo problema ho pensato alla diagonalizzazione. Presumo numeri interi arbitrariamente grandi.

Leggi il primo numero Sinistra pad con zero bit fino a quando non si dispone di 4 miliardi di bit. Se il primo bit (di ordine superiore) è 0, uscita 1; else output 0. (In realtà non è necessario il pad sinistro: si genera semplicemente un 1 se non ci sono abbastanza bit nel numero.) Fare lo stesso con il secondo numero, tranne usare il secondo bit. Continua attraverso il file in questo modo. Verrà emesso un numero di 4 miliardi di bit un bit alla volta e quel numero non sarà uguale a nessun altro nel file. Prova: era lo stesso dell'ennesimo numero, quindi sarebbero d'accordo sull'ennesimo bit, ma non per costruzione.


+1 per la creatività (e il più piccolo output nel caso peggiore per una soluzione a passaggio singolo).
Hmakholm ha lasciato Monica il

Ma non ci sono 4 miliardi di bit su cui diagonalizzare, ce ne sono solo 32. Finirai con un numero a 32 bit che è diverso dai primi 32 numeri nell'elenco.
Brian Gordon,

@Henning Non è quasi un singolo passaggio; devi ancora convertire da unario a binario. Modifica: Beh, immagino sia un passaggio sul file. Non importa.
Brian Gordon,

@Brian, dov'è qualcosa di "unario" qui? La risposta sta costruendo una risposta binaria un po 'alla volta e legge il file di input solo una volta, facendolo passare un solo passaggio. (Se è richiesto l'output decimale , le cose diventano problematiche - quindi probabilmente è meglio costruire una cifra decimale per tre numeri di input e accettare un aumento del 10% nel registro del numero di output).
Hmakholm ha lasciato Monica il

2
@Henning Il problema non ha senso per numeri interi arbitrariamente grandi perché, come molte persone hanno sottolineato, è banale trovare il numero più grande e aggiungerne uno, o costruire un numero molto lungo dal file stesso. Questa soluzione di diagonalizzazione è particolarmente inappropriata perché piuttosto che ramificarsi sul ibit th, si potrebbe semplicemente produrre 1 bit 4 miliardi di volte e lanciarne uno in più alla fine. Sono d'accordo con avere numeri arbitrariamente grandi nell'algoritmo ma penso che il problema sia quello di produrre un numero intero a 32 bit mancante. Non ha senso in nessun altro modo.
Brian Gordon,

6

È possibile utilizzare i flag di bit per contrassegnare se è presente un numero intero o meno.

Dopo aver attraversato l'intero file, scansionare ogni bit per determinare se il numero esiste o meno.

Supponendo che ogni numero intero sia a 32 bit, si adatteranno comodamente a 1 GB di RAM se viene eseguito il flag di bit.


0,5 GB, a meno che tu non abbia ridefinito il byte in modo che sia 4 bit ;-)
dty

2
@dty Penso che significhi "comodamente", dato che ci sarà un sacco di spazio nel 1Gb.
corsiKa

6

Rimuovere lo spazio bianco e i caratteri non numerici dal file e aggiungere 1. Il file ora contiene un singolo numero non elencato nel file originale.

Da Reddit di Carbonetc.


Lo adoro! Anche se non è proprio la risposta che stava cercando ...: D
Johann du Toit,

6

Solo per completezza, ecco un'altra soluzione molto semplice, che molto probabilmente richiederà molto tempo per essere eseguita, ma utilizza pochissima memoria.

Consenti a tutti i possibili numeri interi di essere compreso nell'intervallo da int_mina int_max, e bool isNotInFile(integer)una funzione che restituisce true se il file non contiene un determinato numero intero e false else (confrontando quel determinato numero intero con ciascun numero intero nel file)

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

La domanda riguardava esattamente l'algoritmo per la isNotInFilefunzione. Assicurati di aver compreso la domanda prima di rispondere.
Aleks G,

2
no, la domanda era "quale numero intero non è nel file", non "è numero intero x nel file". una funzione per determinare la risposta a quest'ultima domanda potrebbe ad esempio confrontare ogni numero intero nel file con il numero intero in questione e restituire vero su una corrispondenza.
deg

Penso che questa sia una risposta legittima. Ad eccezione dell'I / O, è necessario solo un numero intero e un flag bool.
Brian Gordon,

@ Aleks G - Non vedo perché questo sia contrassegnato come sbagliato. Siamo tutti d'accordo sul fatto che sia l'algoritmo più lento di tutti :-), ma funziona e richiede solo 4 byte per leggere il file. La domanda originale non prevede che il file possa essere letto una sola volta, ad esempio.
Simon Mourier,

1
@ Aleks G - Giusto. Non ho mai detto neanche tu l'hai detto. Diciamo solo che IsNotInFile può essere banalmente implementato usando un loop sul file: Open; While Not Eof {Leggi intero; Restituisci False se intero = i; Else Continue;}. Richiede solo 4 byte di memoria.
Simon Mourier,

5

Per il vincolo di memoria di 10 MB:

  1. Converti il ​​numero nella sua rappresentazione binaria.
  2. Crea un albero binario dove left = 0 e right = 1.
  3. Inserisci ogni numero nella struttura usando la sua rappresentazione binaria.
  4. Se è già stato inserito un numero, le foglie saranno già state create.

Al termine, basta prendere un percorso che non è stato creato prima per creare il numero richiesto.

4 miliardi di numeri = 2 ^ 32, il che significa che 10 MB potrebbero non essere sufficienti.

MODIFICARE

Un'ottimizzazione è possibile, se sono state create due foglie finali e hanno un genitore comune, possono essere rimosse e il genitore contrassegnato come non una soluzione. Ciò taglia i rami e riduce la necessità di memoria.

EDIT II

Non è necessario costruire anche l'albero completamente. Hai solo bisogno di costruire rami profondi se i numeri sono simili. Se tagliamo anche i rami, questa soluzione potrebbe funzionare davvero.


6
... e come si adatta a 10 MB?
Hmakholm ha lasciato Monica il

Che ne dici di: limitare la profondità del BTree a qualcosa che si adatterebbe a 10 MB; questo significherebbe che si otterrebbero risultati nell'insieme {falso positivo | positivo} e puoi iterarlo e utilizzare altre tecniche per trovare valori.
Jonathan Dickinson,

5

Risponderò alla versione da 1 GB:

Non ci sono abbastanza informazioni nella domanda, quindi dichiarerò prima alcune ipotesi:

L'intero è di 32 bit con un intervallo compreso tra -2.147.483.648 e 2.147.483.647.

Pseudo-codice:

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;
    }
}

4

Finché facciamo risposte creative, eccone un'altra.

Utilizzare il programma di ordinamento esterno per ordinare numericamente il file di input. Funzionerà con qualsiasi quantità di memoria che potresti avere (userà l'archiviazione dei file se necessario). Leggi il file ordinato e visualizza il primo numero mancante.


3

Eliminazione dei bit

Un modo è quello di eliminare i bit, tuttavia ciò potrebbe non produrre effettivamente un risultato (è probabile che non lo sia). psuedocodarlo:

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

Bit conta

Tenere traccia dei conteggi dei bit; e utilizzare i bit con gli importi minimi per generare un valore. Ancora una volta, ciò non ha alcuna garanzia di generare un valore corretto.

Range Logic

Tieni traccia di un elenco di intervalli ordinati (ordinati per inizio). Un intervallo è definito dalla struttura:

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

Passare attraverso ciascun valore nel file e provare a rimuoverlo dall'intervallo corrente. Questo metodo non ha garanzie di memoria, ma dovrebbe funzionare abbastanza bene.


3

2 128 * 10 18 + 1 (che è (2 8 ) 16 * 10 18 + 1) - non può essere una risposta universale per oggi? Ciò rappresenta un numero che non può essere conservato nel file 16 EB, che è la dimensione massima del file in qualsiasi file system corrente.


E come stamperesti il ​​risultato? Non puoi inserirlo in un file e la stampa sullo schermo richiederebbe alcuni miliardi di anni. Probabilmente non è possibile ottenere un uptime con i computer di oggi.
vsz

non si dice mai che dobbiamo stampare il risultato ovunque, semplicemente "generarlo". quindi dipende da cosa intendi per generare. comunque, la mia risposta è solo un trucco per evitare di elaborare un vero algoritmo :)
Michael Sagalovich,

3

Penso che questo sia un problema risolto (vedi sopra), ma c'è un caso laterale interessante da tenere a mente perché potrebbe essere chiesto:

Se ci sono esattamente 4.294.967.295 (2 ^ 32 - 1) numeri interi a 32 bit senza ripetizioni, e quindi ne manca solo uno, esiste una soluzione semplice.

Inizia un totale parziale a zero e per ogni numero intero nel file, aggiungi quel numero intero con overflow a 32 bit (in effetti, runningTotal = (runningTotal + nextInteger)% 4294967296). Una volta completato, aggiungi 4294967296/2 al totale parziale, sempre con overflow a 32 bit. Sottrai questo da 4294967296 e il risultato è l'intero mancante.

Il problema "solo un numero intero mancante" è risolvibile con una sola esecuzione e solo 64 bit di RAM dedicati ai dati (32 per il totale parziale, 32 da leggere nel numero intero successivo).

Corollario: le specifiche più generali sono estremamente semplici da abbinare se non ci occupiamo di quanti bit deve avere il risultato intero. Generiamo semplicemente un numero intero abbastanza grande da non poter essere contenuto nel file che ci viene fornito. Ancora una volta, questo occupa una RAM assolutamente minima. Vedi lo pseudocodice.

# 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";
  }
}

@Nakilon e TheDayTurns lo hanno sottolineato nei commenti alla domanda originale
Brian Gordon,

3

Come ha detto Ryan in pratica, ordina il file e poi vai sugli interi e quando un valore viene saltato lì, ce l'hai :)

EDIT su downvoter: l'OP ha menzionato che il file potrebbe essere ordinato, quindi questo è un metodo valido.


Una parte cruciale è che dovresti farlo mentre vai, in quel modo devi leggere solo una volta. L'accesso alla memoria fisica è lento.
Ryan Amos,

L'ordinamento esterno di @ryan è nella maggior parte dei casi un ordinamento di unione, quindi sull'ultima unione puoi fare il controllo :)
maniaco del cricchetto

Se i dati sono su disco, dovranno essere caricati in memoria. Ciò accade automaticamente dal file system. Se dobbiamo trovare un numero (il problema non indica diversamente), la lettura del file ordinato una riga alla volta è il metodo più efficiente. Utilizza poca memoria e non è più lento di ogni altra cosa: il file deve essere letto.
Tony Ennis,

Come ordinerai 4 miliardi di numeri interi se hai solo 1 GB di memoria? Se usi la memoria virtuale, ci vorrà un po 'di tempo perché i blocchi di memoria vengono cercati dentro e fuori dalla memoria fisica.
Klas Lindbäck,

4
@klas merge sort è progettato per questo
maniaco del cricchetto

2

Se non si assume il vincolo a 32 bit, è sufficiente restituire un numero a 64 bit generato casualmente (o 128 bit se si è pessimisti). La possibilità di collisione è 1 in 2^64/(4*10^9) = 4611686018.4(circa 1 su 4 miliardi). Avresti ragione la maggior parte del tempo!

(Scherzando ... in un certo senso.)


Vedo che questo è già stato suggerito :) voti positivi per quelle persone
Peter Gibson,

Il paradosso del compleanno rende questo tipo di soluzione non degna del rischio, senza controllare il file per vedere se la tua ipotesi casuale era in realtà una risposta valida. (In questo caso il paradosso del compleanno non si applica, ma chiamare ripetutamente questa funzione per generare nuovi valori univoci crea una situazione di paradosso del compleanno.)
Peter Cordes,

@PeterCordes I numeri a 128 bit generati casualmente sono esattamente come funzionano gli UUID - menzionano persino il paradosso del compleanno quando calcolano la probabilità di una collisione nella pagina UUID di
Peter Gibson,

Variante: trova il massimo nel set, aggiungi 1.
Phil

Vorrei eseguire un rapido riassortimento dell'array originale (senza spazio di archiviazione aggiuntivo), quindi marciare attraverso l'array e segnalare il primo intero "ignorato". Fatto. Ha risposto alla domanda
Livello 42
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.