Supponiamo che il file sia così grande che non ci si può permettere di inserirlo nella RAM. Poi, si vorrebbe utilizzare Reservoir Sampling, un algoritmo progettato per gestire la raccolta in modo casuale dalle liste di sconosciuti, lunghezza arbitraria che potrebbero non adattarsi in memoria:
Random r = new Random();
int currentLine = 1;
string pick = null;
foreach (string line in File.ReadLines(filename))
{
if (r.Next(currentLine) == 0) {
pick = line;
}
++currentLine;
}
return pick;
A un livello elevato, il campionamento serbatoio segue una regola di base: ogni l'ulteriore linea ha una possibilità di 1/N di sostituire tutte le linee precedenti.
Questo algoritmo non è intuitivo. Ad un livello alto, funziona facendo in modo che la linea N abbia una possibilità 1/N di sostituire la linea attualmente selezionata. Quindi, la linea 1 ha una probabilità del 100% di essere selezionata, ma una possibilità del 50% di essere successivamente sostituita dalla linea 2.
Ho trovato la comprensione di questo algoritmo per essere più semplice sotto forma di una prova di correttezza. Quindi, una semplice dimostrazione per induzione:
1) Caso di base: tramite ispezione, l'algoritmo funziona se è presente 1 riga.
2) Se l'algoritmo funziona per le linee N-1, l'elaborazione delle linee N funziona perché:
3) Dopo l'elaborazione delle iterazioni N-1 di un file di linee N, tutte le linee N-1 sono ugualmente probabili (probabilità 1/(N -1)).
4) La successiva iterazione assicura che la linea N ha una probabilità di 1/N (perché è quello assegna esplicitamente l'algoritmo, ed è l'iterazione finale), riducendo la probabilità di tutte le linee precedenti:
1/(N-1) * (1-(1/N))
1/(N-1) * (N/N-(1/N))
1/(N-1) * (N-1)/N
(1*(N-1))/(N*(N-1))
1/N
Se si conosce il numero di righe presenti nel file in anticipo, questo algoritmo è più costoso del necessario, poiché legge sempre l'intero file.
Cercare un offset casuale nel file, quindi eseguire la scansione in avanti per un carattere di nuova riga. Leggi i dati fino alla prossima nuova riga. Prendi le tue precauzioni con la fine del file. La probabilità non sarà uniforme se le linee hanno grandi differenze di lunghezza però. Oh, e 5000 non è * quello * molto ;-) –
Spezzalo a 50 file con 100 linee ciascuno, num. Casuale 0-50 per file, riga casuale 0-99 per riga. Detto questo, leggere 5000 righe ogni 5 minuti non è ancora un grosso problema ... non efficiente, ma non un vero problema. Se questo è il tuo unico problema con l'app, sei buono :) – Noctis
quanto è grande il file in totale? – olydis