Esercizio - Count-Min sketch - tabella a mano e dimensionamento
Teoria: Algoritmi di streaming - campionamento di riserva e conteggio degli elementi distintiModello a flusso (un passaggio, memoria molto minore di n); campionamento di riserva (reservoir sampling) con dimostrazione per induzione; contatore di Morris (stima non distorta 2^X-1, varianza n(n-1)/2); elementi distinti: idea di Flajolet-Martin e stima del k-esimo minimo (k-1)/v_k; Count-Min sketch con garanzia (epsilon, delta) e dimensionamento w=e/epsilon, d=ln(1/delta).Algoritmi di streaming - campionamento di riserva e conteggio degli elementi distinti → (Count-Min sketch), Hashing randomizzato - famiglie universali, filtri di Bloom e fingerprintHashing con catene e costo atteso 1+alfa; famiglie universali (probabilità di collisione al più 1/m) e costruzione h(x)=((ax+b) mod p) mod m con dimostrazione; hashing perfetto a due livelli (spazio lineare, ricerca O(1) nel caso peggiore); filtri di Bloom: falsi positivi (1-e^(-kn/m))^k e k ottimo (m/n) ln 2; fingerprint di stringhe e numero di bit necessari.Hashing randomizzato - famiglie universali, filtri di Bloom e fingerprint → (famiglie universali).
Testo.
- Uno sketch ha righe e colonne. Il flusso contiene cinque volte, tre volte, due volte, e una volta ( arrivi). Le funzioni hash (date) sono
Costruire la tabella dei contatori e le stime per i cinque elementi. Quali sono sovrastimati e perché? Che stima dà lo sketch per un elemento mai visto con , ? 2. Dimostrare che e che, con e , . 3. Dimensionare uno sketch per , e per , : quanti contatori? 4. Simulazione: flusso di arrivi su valori con frequenze di tipo Zipf (esponente ). Per i tre sketch , e misurare l'errore massimo e medio sui valori, la frazione di valori con errore superiore a e l'errore sull'elemento più frequente.
Soluzione
1. Tabella. Riga (somma per colonna di ): colonna : ; colonna : ; colonna : ; colonna : . Riga : colonna : ; colonna : ; colonna : ; colonna : . Stime:
- (vero : sovrastimato di , cioè : nella riga collide con , nella riga con , e il minimo è l'errore minore).
- ✓ (la riga non ha collisioni).
- ✓; ✓; ✓.
- Elemento mai visto con , : (vero ). Se invece fosse , si avrebbe : gli errori additivi possono essere grandi per gli elementi rari. Controllo: ogni riga somma a (riga : ; riga : ).
2. Garanzia. Nella riga la cella di contiene con , quindi ogni cella è e il minimo anche: . Per l'universalità e per la linearità (con ). Markov (con ): . Le righe usano funzioni indipendenti, quindi se .
3. Dimensionamento.
- , : , : contatori (circa KB con contatori a bit).
- , : , (): contatori.
- , : , : contatori.
4. Risultati della simulazione (indipendenti dal numero di valori ):
| errore max | errore medio | frazione con errore | elemento più frequente () | |||
|---|---|---|---|---|---|---|
| stima (errore ) | ||||||
| stima (errore ) | ||||||
| stima (errore ) |
La frazione di valori che violano il limite (, , ) è molto sotto (, , ): il limite è prudente. Il terzo sketch, piccolissimo, ha un errore massimo di (maggiore di ) su un valore le cui celle sono contaminate dai valori più frequenti: con colonne e righe le collisioni sono inevitabili. L'errore additivo è però trascurabile sull'elemento più frequente ( della sua frequenza al massimo): per questo lo sketch serve a trovare gli elementi frequenti, non i rari.
Codice di verifica
import random
from math import ceil, e, log
h1 = {'a': 0, 'b': 1, 'c': 0, 'd': 2, 'e': 3}
h2 = {'a': 1, 'b': 1, 'c': 3, 'd': 0, 'e': 2}
f = {'a': 5, 'b': 3, 'c': 2, 'd': 1, 'e': 1}
C = [[0] * 4 for _ in range(2)]
for x, c in f.items():
C[0][h1[x]] += c
C[1][h2[x]] += c
print(C, {x: min(C[0][h1[x]], C[1][h2[x]]) for x in f})
print(ceil(e / 0.001), ceil(log(100)), ceil(e / 0.01), ceil(log(20)), ceil(e / 0.05), ceil(log(5)))
def simula(eps, delta, N=200_000, U=20_000, seed=0):
rnd = random.Random(seed)
P = (1 << 61) - 1
w, d = ceil(e / eps), ceil(log(1 / delta))
ab = [(rnd.randrange(1, P), rnd.randrange(P)) for _ in range(d)]
flusso = rnd.choices(range(U), weights=[1 / (i + 1) ** 1.1 for i in range(U)], k=N)
C, fr = [[0] * w for _ in range(d)], {}
for x in flusso:
fr[x] = fr.get(x, 0) + 1
for j, (a, b) in enumerate(ab):
C[j][((a * x + b) % P) % w] += 1
err = [min(C[j][((a * x + b) % P) % w] for j, (a, b) in enumerate(ab)) - fr.get(x, 0) for x in range(U)]
top = max(fr, key=fr.get)
return w * d, max(err), sum(err) / U, sum(r > eps * N for r in err) / U, fr[top], err[top]
for eps, delta in ((0.01, 0.05), (0.001, 0.01), (0.05, 0.2)):
print(eps, delta, simula(eps, delta))Risultato: la matrice [[7, 3, 1, 1], [1, 8, 1, 2]] con stime {'a': 7, 'b': 3, 'c': 2, 'd': 1, 'e': 1}, le dimensioni 2719 5 272 3 55 2, e le tre righe (celle, errore max, errore medio, frazione, f max, errore su f max) della tabella (per esempio 13595 44 7.2497 0.0 29285 3).
Versione ripasso
- Tabella (, , ; : , , , ; : , , , ): . Stime (vero , sovrastima ), , , ; elemento assente con celle : (Algoritmi di streaming - campionamento di riserva e conteggio degli elementi distintiModello a flusso (un passaggio, memoria molto minore di n); campionamento di riserva (reservoir sampling) con dimostrazione per induzione; contatore di Morris (stima non distorta 2^X-1, varianza n(n-1)/2); elementi distinti: idea di Flajolet-Martin e stima del k-esimo minimo (k-1)/v_k; Count-Min sketch con garanzia (epsilon, delta) e dimensionamento w=e/epsilon, d=ln(1/delta).Algoritmi di streaming - campionamento di riserva e conteggio degli elementi distinti →).
- Garanzia. Cella , , ; : Markov ; righe indipendenti: con .
- Dimensioni. : ; : ; : .
- Simulazione (, valori Zipf): errore max , , ; medio , , ; violazioni di : , , (limite : , , ); il più frequente (): errori , , .
- Errori tipici. Usare la media delle righe al posto del minimo; prendere ; leggere come errore relativo (Hashing randomizzato - famiglie universali, filtri di Bloom e fingerprintHashing con catene e costo atteso 1+alfa; famiglie universali (probabilità di collisione al più 1/m) e costruzione h(x)=((ax+b) mod p) mod m con dimostrazione; hashing perfetto a due livelli (spazio lineare, ricerca O(1) nel caso peggiore); filtri di Bloom: falsi positivi (1-e^(-kn/m))^k e k ottimo (m/n) ln 2; fingerprint di stringhe e numero di bit necessari.Hashing randomizzato - famiglie universali, filtri di Bloom e fingerprint →).