Come funziona l'algoritmo HyperLogLog?


172

Recentemente ho imparato a conoscere diversi algoritmi nel mio tempo libero e uno che mi sono imbattuto e che sembra essere molto interessante si chiama algoritmo HyperLogLog - che stima quanti elementi unici sono in un elenco.

Questo è stato particolarmente interessante per me perché mi ha riportato ai miei giorni su MySQL quando ho visto quel valore di "Cardinalità" (che ho sempre ipotizzato fino a poco tempo fa che non era stato calcolato).

Quindi so come scrivere un algoritmo in O ( n ) che calcolerà quanti elementi unici sono presenti in un array. Ho scritto questo in JavaScript:

function countUniqueAlgo1(arr) {
    var Table = {};
    var numUnique = 0;
    var numDataPoints = arr.length;
    for (var j = 0; j < numDataPoints; j++) {
        var val = arr[j];
        if (Table[val] != null) {
            continue;
        }
        Table[val] = 1;
        numUnique++;
    }
    return numUnique;
}

Ma il problema è che il mio algoritmo, mentre O ( n ), usa molta memoria (memorizzando valori in Table).

Ho letto questo documento su come contare i duplicati in un elenco in O ( n ) tempo e usando la memoria minima.

Spiega che eseguendo l'hashing e contando i bit o qualcosa si può stimare entro una certa probabilità (supponendo che l'elenco sia distribuito uniformemente) il numero di elementi unici in un elenco.

Ho letto il documento, ma non riesco a capirlo. Qualcuno può dare una spiegazione più profana? So cosa sono gli hash, ma non capisco come vengano utilizzati in questo algoritmo HyperLogLog.


4
Questo documento ( research.google.com/pubs/pub40671.html ) riassume anche l'algoritmo HyperLogLog e alcuni miglioramenti. Penso che sia più facile da capire rispetto al documento originale.
zhanxw,

11
Solo un suggerimento sulla nomenclatura: alcune persone usano il set di parole per descrivere una raccolta di oggetti unici . Per loro, la tua domanda potrebbe avere più senso se hai usato invece il termine elenco o array.
Paddy3118,

Risposte:


153

Il trucco principale dietro questo algoritmo è che se, osservando un flusso di numeri interi casuali, vedi un numero intero la cui rappresentazione binaria inizia con un prefisso noto, è più probabile che la cardinalità del flusso sia 2 ^ (dimensione del prefisso) .

Cioè, in un flusso casuale di numeri interi, ~ 50% dei numeri (in binario) inizia con "1", il 25% inizia con "01", il 12,5% inizia con "001". Ciò significa che se osservi un flusso casuale e vedi un "001", c'è una maggiore possibilità che questo flusso abbia una cardinalità di 8.

(Il prefisso "00..1" non ha alcun significato speciale. È lì solo perché è facile trovare il bit più significativo in un numero binario nella maggior parte dei processori)

Naturalmente, se si osserva un solo numero intero, la probabilità che questo valore sia errato è alta. Ecco perché l'algoritmo divide lo stream in substrati indipendenti "m" e mantiene la lunghezza massima di un prefisso "00 ... 1" visto di ciascun substream. Quindi, stima il valore finale prendendo il valore medio di ciascun substream.

Questa è l'idea principale di questo algoritmo. Ci sono alcuni dettagli mancanti (la correzione per valori di stima bassi, ad esempio), ma è tutto ben scritto nel documento. Ci scusiamo per il terribile inglese.


"Esiste una maggiore possibilità che questo flusso abbia una cardinalità di 8" Puoi spiegare perché 000 indica il numero previsto di prove 2 ^ 3. Ho provato a calcolare l'aspettativa matematica del numero di prove supponendo che abbiamo almeno una corsa con 3 zeri e nessuna corsa con 4 zeri ...
yura

5
Non ho capito bene il documento fino a quando non ho letto questo. Adesso ha senso.
josiah

5
@yura So che è un commento molto vecchio, ma può essere utile per altre persone. Ha detto "Cioè, in un flusso casuale di numeri interi, (...) il 12,5% inizia con" 001 "." La probabile cardinalità è 8 perché il 12,5% rappresenta un ottavo di tutto il flusso.
braunmagrin,

111

Un HyperLogLog è una struttura di dati probabilistica . Conta il numero di elementi distinti in un elenco. Ma rispetto a un modo semplice di farlo (avere un set e aggiungere elementi al set) lo fa in modo approssimativo.

Prima di vedere come funziona l'algoritmo HyperLogLog, bisogna capire perché ne hai bisogno. Il problema con un modo semplice è che consumaO(distinct elements) spazio. Perché qui c'è una grande notazione O anziché solo elementi distinti? Questo perché gli elementi possono avere dimensioni diverse. Un elemento può essere 1un altro elemento "is this big string". Quindi se hai un enorme elenco (o un enorme flusso di elementi) ci vorrà molta memoria.


Conteggio probabilistico

Come si può ottenere una stima ragionevole di un numero di elementi unici? Supponiamo di avere una stringa di lunghezza mche consiste {0, 1}con uguale probabilità. Qual è la probabilità che inizi con 0, con 2 zeri, con k zeri? Lo è 1/2, 1/4e 1/2^k. Ciò significa che se hai incontrato una stringa con kzeri, hai guardato approssimativamente attraverso gli 2^kelementi. Quindi questo è un buon punto di partenza. Avere un elenco di elementi che sono equamente distribuiti tra0 e 2^k - 1puoi contare il numero massimo del più grande prefisso di zeri nella rappresentazione binaria e questo ti darà una stima ragionevole.

Il problema è che l'ipotesi di distribuire uniformemente numeri da 0t 2^k-1è troppo difficile da raggiungere (i dati che abbiamo incontrato non sono per lo più numeri, quasi mai distribuiti uniformemente e possono essere tra qualsiasi valore. Ma usando una buona funzione di hashing puoi supporre che i bit di output sarebbero distribuiti uniformemente e la maggior parte della funzione di hashing ha output tra 0e 2^k - 1( SHA1 fornisce valori tra 0e la carta di conteggio probabilistica del 1984 (è un po 'più intelligente con la stima, ma siamo ancora vicini).2^160 ). Ciò che abbiamo ottenuto finora è che possiamo stimare il numero di elementi unici con la massima cardinalità dei kbit memorizzando solo un numero di log(k)bit di dimensioni . Il rovescio della medaglia è che abbiamo una grande varianza nella nostra stima. Una cosa interessante che abbiamo quasi creato

loglog

Prima di andare oltre, dobbiamo capire perché la nostra prima stima non è eccezionale. Il motivo è che una ricorrenza casuale di elemento 0-prefisso ad alta frequenza può rovinare tutto. Un modo per migliorarlo è usare molte funzioni di hash, contare il massimo per ciascuna delle funzioni di hash e alla fine farne una media. Questa è un'idea eccellente, che migliorerà la stima, ma la carta LogLog ha usato un approccio leggermente diverso (probabilmente perché l'hashing è un po 'costoso).

Hanno usato un hash ma lo hanno diviso in due parti. Uno si chiama bucket (il numero totale di bucket è 2^x) e un altro - è sostanzialmente lo stesso del nostro hash. È stato difficile per me ottenere quello che stava succedendo, quindi farò un esempio. Supponi di avere due elementi e la tua funzione hash che dia forma 0ai 2^10valori prodotti 2: 344e 387. Hai deciso di avere 16 secchi. Quindi hai:

0101 011000  bucket 5 will store 1
0110 000011  bucket 6 will store 4

Avendo più secchi si riduce la varianza (si utilizza leggermente più spazio, ma è ancora minuscolo). Usando le abilità matematiche sono stati in grado di quantificare l'errore (che è 1.3/sqrt(number of buckets)).

HyperLogLog

HyperLogLog non introduce nuove idee, ma utilizza principalmente molta matematica per migliorare la stima precedente. I ricercatori hanno scoperto che se si rimuove il 30% dei numeri più grandi dai bucket, si migliora significativamente la stima. Hanno anche usato un altro algoritmo per la media dei numeri. Il documento è pesante per la matematica.


E voglio finire con un recente documento, che mostra una versione migliorata dell'algoritmo hyperLogLog (fino ad ora non ho avuto il tempo di comprenderlo appieno, ma forse in seguito migliorerò questa risposta).


2
Presumo che teoricamente k zeroesnon sia una cosa speciale. puoi invece cercare k onese la logica sarebbe la stessa o addirittura cercare la k lengthstringa di {0,1}ma prendere una di queste stringhe e attenersi ad essa? perché tutti hanno uguale probabilità di 1/2 ^ k in caso di tali stringhe binarie?
user881300

3
HyperLogLog non rimuove il 30% dei numeri più grandi. Questa è l'idea dell'algoritmo SuperLogLog descritta anche nel documento LogLog. L'idea principale dell'algoritmo HyperLogLog è di calcolare la media della potenza di due usando la media armonica anziché la media geometrica utilizzata da SuperLogLog e LogLog.
otmar,

21

L'intuizione è che se l'input è un grande insieme di numeri casuali (ad es. Valori con hash), dovrebbero distribuire uniformemente su un intervallo. Supponiamo che l'intervallo sia fino a 10 bit per rappresentare un valore fino a 1024. Quindi osservato il valore minimo. Diciamo che è 10. Quindi la cardinalità sarà stimata in circa 100 (10 × 100 ≈ 1024).

Leggi l'articolo per la vera logica ovviamente.

Un'altra buona spiegazione con il codice di esempio può essere trovata qui:
Algoritmi dannatamente cool: stima della cardinalità - Nick's Blog


3
votato per il link al post dannatamente cool sul blog degli algoritmi. questo mi ha davvero aiutato a capire l'algoritmo.
Igor Serebryany,
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.