Analiza Probabilistă în Hashing
În acest laborator vom explora comportamentul probabilist al hashing-ului și fenomenele care apar din cauza coliziunilor. Vom lucra pe două exemple simple care ilustrează concepte teoretice importante:
- Paradoxul zilelor de naștere — probabilitatea unei coliziuni într-un spațiu finit de valori;
- Problema colecționarului de cupoane — timpul așteptat până acoperim toate cazurile posibile.
Context
În hashing, fiecare element este atribuit aleatoriu unui dintre cele ( k ) sloturi. Problemele pe care le studiem astăzi au aplicații directe în analiza performanței tabelelor hash.
Exemplul 1: Paradoxul zilelor de naștere
Scenariu: avem ( n ) persoane și ( k = 365 ) zile posibile. Dorim să aflăm probabilitatea ca cel puțin două persoane să aibă aceeași zi de naștere.
Formula teoretică este:
$$ P(\text{coliziune}) = 1 - \frac{k!}{(k-n)! \, k^n} $$
Codul de mai jos simulează fenomenul:
- paradoxul_nasterilor.py
import random def paradox_zile_nastere(trials=10000, grup=23, zile=365): """Estimează probabilitatea ca două persoane să aibă aceeași zi de naștere.""" count = 0 for _ in range(trials): zile_aleatoare = [random.randint(1, zile) for _ in range(grup)] if len(zile_aleatoare) != len(set(zile_aleatoare)): count += 1 return count / trials for n in [10, 20, 23, 30, 40, 50]: p = paradox_zile_nastere(grup=n) print(f"Grup de {n} persoane → Probabilitate ≈ {p:.3f}")
Întrebări:
1. Pentru ce valoare a lui ( n ) probabilitatea depășește 0.5? 2. Cum se aseamănă acest fenomen cu coliziunile dintr-o tabelă hash?
Exemplul 2: Problema colecționarului de cupoane
Scenariu: avem ( n ) cupoane distincte. La fiecare pas tragem unul aleator (uniform). Dorim să aflăm câte extrageri sunt necesare în medie până le avem pe toate.
Rezultatul teoretic: $$ E[T] = n H_n = n(1 + \tfrac{1}{2} + \tfrac{1}{3} + \dots + \tfrac{1}{n}) $$
Simulare:
- cupoane.py
import random def colector_cupoane(n, incercari=500): """Returnează numărul mediu de extrageri până la colectarea tuturor cupoanelor.""" total = 0 for _ in range(incercari): colectate = set() pasi = 0 while len(colectate) < n: colectate.add(random.randint(0, n-1)) pasi += 1 total += pasi return total / incercari for n in [10, 50, 100]: estimat = colector_cupoane(n) print(f"n = {n}: medie ≈ {estimat:.2f}")
Întrebări:
1. Cum crește numărul așteptat de extrageri odată cu ( n )? 2. Ce analogie există între această problemă și procesul de „umplere” a tuturor sloturilor într-o tabelă hash?
Rezumat
- Hashing-ul se poate analiza folosind concepte probabiliste simple.
- Paradoxul zilelor de naștere arată cât de rapid apar coliziunile.
- Problema colecționarului de cupoane arată cât de lent se „umple” complet un spațiu finit.