===== 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:
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:
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.