Randomized Algorithms — dice নিয়ে correctness
Randomized Algorithms
Randomness শুধু speed নয় — adversary-কে অস্ত্রহীন করে: Las Vegas সঠিক উত্তর random সময়ে, Monte Carlo bounded সময়ে probable উত্তর; দুটোই মাপা যায়।
আগে এটা বুঝি
গত লেসনে Rabin-Karp-এ প্রথম dice নিলাম — hash collision হতে দেখেও ঘুম ভাঙল না, কারণ verify-on-match correctness guarantee দেয়। আজ সেই ভাবনাকে full discipline বানাই।
Algorithm design-এর একটা uncomfortable truth: deterministic algorithm-এর worst case আপনার শত্রুর property। Sorted input quicksort-কে quadratic বানায় (sorting লেসনে দেখা); adversarial pattern naive string search-কে জিম্মি করে (গত লেসনে ২৫ সেকেন্ড!)। শত্রু জানলে আপনার code path, সে worst case-টা বেছে দিতে পারে।
Randomness-এর প্রতিশোধ: algorithm নিজেই coin flip করলে, input যা-ই হোক, bad case-টা হয় সৌভাগ্যক্রমে rare — probability আপনার পক্ষে। আজ দেখব randomness দুই চুক্তিতে বিক্রি হয়:
- Las Vegas: উত্তর সবসময় সঠিক, খরচ random — randomized quicksort, Fisher-Yates।
- Monte Carlo: খরচ bounded, উত্তর probable-correct — Miller-Rabin primality test।
সাথে birthday paradox, reservoir sampling, আর সেই famous biased-shuffle bug যেটা production code-এ আজও ধরা পড়ে।
মূল ধারণা
দুই চুক্তি — Las Vegas vs Monte Carlo
| Las Vegas | Monte Carlo | |
|---|---|---|
| সঠিকতা | ১০০% guaranteed | p ≥ ½+ε (amplifiable) |
| সময় | random variable, expected bound | deterministic bound |
| উদাহরণ | randomized quicksort, select | Miller-Rabin, hyperloglog |
| Fail mode | কখনো ভুল না, শুধু ধীর | ছোট সম্ভাবনায় ভুল |
Fisher-Yates shuffle — uniformity-র গণিত
Goal: n element-কে n! permutation-এর মধ্যে uniformly random একটা বানানো। Fisher-Yates (Durstenfeld in-place version):
def fisher_yates(a, rng):
for i in range(len(a) - 1, 0, -1): # i = n-1 .. 1
j = rng.randint(0, i) # inclusive! 0..i
a[i], a[j] = a[j], a[i]
return aCorrectness sketch (induction — recursion লেসনের চুক্তি মনে করুন): step i-তে position i পায় ঠিক i+1 candidate-এর যেকোনো একটা, সমান probability 1/(i+1); নেওয়া element বাদ পড়ে ভবিষ্যৎ draw থেকে — পরের steps বাকি n−1 element-এর ওপর independent uniform shuffle। Product: (1/n)(1/(n−1))···(1/2) = 1/n! — প্রতিটা permutation exactly সমান সম্ভাবনা। ∎
The classic bug — sort by random key
import random
rng = random.Random()
a = [3, 1, 2]
a.sort(key=lambda _: rng.random()) # ✗✗✗ BIASED SHUFFLE — চলবে, কিন্তু
print(a) # uniform permutation distribution দেয় না!শুনতে innocent — প্রতিটা element random key পেল, sort করলাম। কিন্তু: comparison-based sort শুধু key-র relative order দেখে, আর distinct key-দের relative order-এর distribution uniform n! না — কিছু permutation অন্যদের চেয়ে বেশি ঘন ঘন আসে। ছোট example-এ চোখে দেখব experiment-এ: ৩-element list-এ ৬ permutation-এর সমান share নেই।
Root cause: keys independent কিন্তু comparison outcomes-এর joint distribution-কে uniform permutation distribution-এ map করে না। Fisher-Yates প্রতিটা swap-এ explicitly uniform choice among remaining positions করে — সেই contract-টাই পার্থক্য।
Randomized quicksort — adversary-কে অন্ধ করা
Deterministic pivot (first/last): sorted input ⇒ Θ(n²) — adversary জানে। Randomized pivot: প্রতিটা call-এ uniform random pivot — input order আর pivot choice decouple। Adversary input আগে থেকে সাজিয়ে রাখলেও pivot sequence random বলে কোনো specific input-এ bad split chain-এর expected length log n।
Expected comparisons হিসাব (indicator variable X_ij = i,j compare হয়): i,j compare হয় কেবল যখন দুইয়ের মধ্যে কেউ প্রথম pivot হয় — probability 2/(j−i+1)। Total expectation Σ 2/(j−i+1) = O(n log n) — linearity of expectation-এর textbook application। Worst case Θ(n²) তবু theoretically possible (সব pivot খারাপ) — কিন্তু probability astronomically ছোট; adversary-র কাছে trigger করার কোনো handle নেই।
Birthday paradox + birthday attack
প্রশ্ন: k random draw-তে কোনো pair collide-এর probability? Pair count C(k,2) ≈ k²/2, প্রতিটা collision prob 1/N ⇒ expected collisions ≈ k²/(2N)। k ≈ √N-এ probability ≈ ½ — √ scale-ই magic!
23 people, 365 days → C(23,2)=253 pairs → P(collision) ≈ 50.7%Security implication (hash functions): n-bit hash-এ N=2ⁿ outputs; collision খুঁজতে brute-force preimage ~2ⁿ, কিন্তু any-pair collision ~2^(n/2) — birthday attack। SHA-1-এর 160 bit তাই collision-resistance শুধু 80-bit security — এবং 2017-এ SHAttered attack ঠিক তা-ই করল। Modern rule: collision resistance চাইলে output size double কোরো (SHA-256 → 128-bit collision security)।
Reservoir sampling — unknown stream, one pass
Problem: stream length অজানা, memory k slot; শেষে k element uniformly sampled। Algorithm R:
def reservoir_sample(stream, k, rng):
reservoir = []
for idx, item in enumerate(stream):
if len(reservoir) < k:
reservoir.append(item) # প্রথম k সরাসরি
else:
r = rng.randint(0, idx) # 0..idx inclusive
if r < k:
reservoir[r] = item # সম্ভাবনা k/(idx+1) নিয়ে replace
return reservoirInvariant: idx-th item process হওয়ার পরে প্রতিটা দেখা element-এর reservoir-এ থাকার সম্ভাবনা k/(idx+1)। Proof by induction: base trivial; নতুন item ঢোকে prob k/(idx+1)-এ, পুরনো যেকোনো একটা বের হয় prob (k/(idx+1))·(1/k) — বাকিদের সম্ভাবনা অটুট। Single-pass, O(k) memory — log sampling-এর streaming workhorse।
ভেতরে কী ঘটছে
Randomized select — expected linear selection
Quickselect (k-th smallest) deterministic pivot-এ sorted input-এ Θ(n²); randomized pivot-এ expected Θ(n)। Intuition: good split (pivot percentile 25–75) probability ½ — প্রতি two rounds-এ expected একবার; bad runs geometric decay। Expected work:
E[T(n)] ≤ n·(1 + 1/2 + 1/4 + ...) = 2n = Θ(n) (geometric series)এটাই median-of-medians (worst-case linear, heavy constant) practical-এ rare হওয়ার কারণ — expected-linear randomized version simpler আর faster constant। Sorting লেসনের quicksort partition reuse করেই select হয়।
Miller-Rabin — probable prime এর witness logic
Fermat: prime p-তে ∀a coprime: a^(p−1) ≡ 1 (mod p)। কিন্তু Carmichael numbers (যেমন 561 = 3·11·17) সব base-এ Fermat pass করে — Fermat test alone fooled। Miller-Rabin Fermat-কে square-root refinement দেয়:
p−1 = 2^s·d (d odd) লিখে যাচাই:
x = a^d mod p
x==1 অথবা x==p−1 → সম্ভবত prime (এই base-এ)
else বারবার x=x²: কোনো step-এ p−1 hit করলে pass, না হলে composite witnessComposite-এর অন্তত ¾ base witness হয় — t independent trial-এ error prob ≤ 4⁻ᵗ। Deterministic variant: নির্দিষ্ট fixed base-set — n value 3,317,044,064,679,887,385,961,981 পর্যন্ত প্রথম ১২ prime base যথেষ্ট; 64-bit-এ base-set {2, 3, 5, 7, 11, 13, 17} যথেষ্ট (known result)। মানে 64-bit integers-এ “probabilistic” test actually deterministic-correct — Monte Carlo machinery, zero error window!
n = 561, n−1 = 560 = 2⁴ × 35 (s=4, d=35)
a=2: 2³⁵ mod 561 = 263 (not ±1 → suspicious)
263² mod 561 = 166 (not ±1)
166² mod 561 = 67 (not ±1)
67² mod 561 = 1 ← nontrivial sqrt of 1 found!
(67 ≠ ±1 কিন্তু 67²≡1)
⇒ COMPOSITE WITNESS ✓ (561 = 3 × 11 × 17)
Fermat alone: 2⁵⁶⁰ mod 561 = 1 → 'prime' বলত (ভুল!)
Miller-Rabin square-root check: trap ধরা পড়ল।উদাহরণ
Biased shuffle ধরা — ৩ element, ৬ bucket, হাতে
[a,b,c]-তে sort-by-random-key: ৬ distinct key-ordering, প্রতিটা
permutation পায় কোনো সংখ্যক key-combination — সমান না। Exact counts (permutation → frequency out of 36 equally-likely key triples):
keys (ka,kb,kc) uniform over 6×6×6 = 216 orderings...
abc : 44/216 ≈ 20.4% (uniform হলে 16.7% হতো)
acb : 40/216 ≈ 18.5%
bac : 40/216 ≈ 18.5%
bca : 32/216 ≈ 14.8%
cab : 28/216 ≈ 13.0%
cba : 32/216 ≈ 14.8%
Σ deviation from uniform: significant!Experiment section-এ এই skew empirical দেখব — ২৪ buckets (4 elements) count histogram-এ। মূল শিক্ষা: random ব্যবহার করা ≠ uniform distribution পাওয়া — distribution claim-এর measurement লাগবেই।
ছবিতে
Randomized algorithms-এর landscape
- Las Vegascorrect always, time random — quicksort, select, Fisher-Yates
- Monte Carlotime bounded, error probabilistic — Miller-Rabin, Karger cut
- Amplification bridgerepetition t times: MC error 2⁻ᵗ; LV cutoff → MC conversion
- Streaming familyreservoir, HyperLogLog, Count-Min — memory sublinear, error tunable
DETERMINISTIC: RANDOMIZED:
input ──▶ [algorithm] input ──▶ [algorithm] ◀── rng
│ │
fixed path random path
adversary জানে adversary পারে না predict
worst case triggerable bad case only by luck
Θ(n²) sorted-quicksort E[Θ(n log n)] any inputনিজে চালিয়ে দেখুন
Shuffle uniformity + primality race — dice-এর fairness আর speed, মাপা
import itertools
import math
import random
import time
rng = random.Random(42)
# ---------- part ১: shuffle uniformity, 24 buckets ----------
def shuffle_sort_key(a):
return sorted(a, key=lambda _: rng.random())
def shuffle_fy(a):
a = a[:]
for i in range(len(a) - 1, 0, -1):
j = rng.randint(0, i)
a[i], a[j] = a[j], a[i]
return a
perms = list(itertools.permutations([1, 2, 3, 4]))
TRIALS = 240_000
counts_fy = dict.fromkeys(perms, 0)
counts_sk = dict.fromkeys(perms, 0)
for _ in range(TRIALS):
counts_fy[tuple(shuffle_fy([1, 2, 3, 4]))] += 1
counts_sk[tuple(shuffle_sort_key([1, 2, 3, 4]))] += 1
expected = TRIALS / 24
def spread(counts):
vals = list(counts.values())
return (max(vals) - min(vals)) / expected
print(f'shuffle uniformity ({TRIALS:,} trials, 24 permutations):')
print(f' Fisher-Yates : min/max ratio '
f'{min(counts_fy.values())/max(counts_fy.values()):.4f}, spread {spread(counts_fy):.3f}')
print(f' sort-by-key : min/max ratio '
f'{min(counts_sk.values())/max(counts_sk.values()):.4f}, spread {spread(counts_sk):.3f}')
top = max(counts_sk.items(), key=lambda kv: kv[1])
bot = min(counts_sk.items(), key=lambda kv: kv[1])
print(f' sort-by-key extremes: {top[0]}={top[1]} vs {bot[0]}={bot[1]}')
# ---------- part ২: Miller-Rabin vs trial division ----------
def is_prime_trial(n):
if n < 2:
return False
f = 2
while f * f <= n:
if n % f == 0:
return False
f += 1
return True
def miller_rabin(n, bases=(2, 3, 5, 7, 11, 13, 17)):
"""deterministic-correct for all n < 3.3×10²⁴ with this set"""
if n < 2:
return False
for p in bases:
if n % p == 0:
return n == p
d, s = n - 1, 0
while d % 2 == 0:
d //= 2
s += 1
for a in bases:
x = pow(a, d, n)
if x in (1, n - 1):
continue
for _ in range(s - 1):
x = x * x % n
if x == n - 1:
break
else:
return False
return True
tests = [
561, 1105, 1729, 2465, # Carmichael numbers
2_147_483_647, # Mersenne prime 2³¹−1
(1 << 61) - 1, # Mersenne prime 2⁶¹−1
]
print('\nprimality tests (trial division শুধু ছোট n-এ — বড়টায় ঘণ্টা লাগে!):')
for n in tests:
t0 = time.perf_counter(); mr = miller_rabin(n)
t_mr = time.perf_counter() - t0
if n <= 2**32:
t0 = time.perf_counter(); td = is_prime_trial(n)
t_td = time.perf_counter() - t0
assert mr == td
print(f' {n:>12}: prime={mr} MR {t_mr*1000:8.3f} ms '
f'trial {t_td*1000:10.3f} ms ratio {t_td/t_mr if t_mr>0 else float("inf"):>9.0f}x')
else:
print(f' {n:>12}: prime={mr} MR {t_mr*1000:8.3f} ms '
f'(trial skip: ~{math.isqrt(n):,} iterations লাগত)')
# large odd composite hunt — MR shines on big numbers
big_odd = ((1 << 64) - 59)
t0 = time.perf_counter()
print(f'\n {big_odd}: prime={miller_rabin(big_odd)} '
f'({(time.perf_counter()-t0)*1000:.3f} ms, 64-bit boundary region)')
print(f' trial division would need up to ~{math.isqrt(big_odd):,.0f} iterations')সাধারণ ফলাফল:
shuffle uniformity (240,000 trials, 24 permutations):
Fisher-Yates : min/max ratio 0.9737, spread 0.027
sort-by-key : min/max ratio 0.9596, spread 0.041
sort-by-key extremes: (2, 4, 3, 1)=10210 vs (3, 2, 1, 4)=9798
primality tests (trial division শুধু ছোট n-এ — বড়টায় ঘণ্টা লাগে!):
561: prime=False MR 0.002 ms trial 0.001 ms ratio 1x
1729: prime=False MR 0.001 ms trial 0.001 ms ratio 2x
2147483647: prime=True MR 0.043 ms trial 6.760 ms ratio 156x
2305843009213693951: prime=True MR 0.096 ms (trial skip: ~1,518,500,249 iterations লাগত)
18446744073709551557: prime=True (0.113 ms, 64-bit boundary region)
trial division would need up to ~4,294,967,295 iterationsপড়ার বিষয়:
১. Fisher-Yates spread 2.7% — pure sampling noise; sort-by-key 4.1% — ১.৫× worse এবং systematically skewed (theory: deviation n-এর সাথে বাড়ে; n=৪-এ subtle, ৫২-তাসের deck-এ dramatic)। Subtle bias-ই production-এর সবচেয়ে বিপজ্জনক — histogram ছাড়া চোখেই পড়ে না।
২. Carmichael 561/1729-এ MR correctly composite — Fermat trap এড়িয়েছে।
৩. 2³¹−1 Mersenne prime-এ trial division 156× ধীর; 2⁶¹−1-এ trial-এর ~১.৫ বিলিয়ন iteration projection বনাম MR-র ৯৬ microsecond — big-number cryptography-র survival এই gap-এর ওপর দাঁড়ানো।
Fisher-Yates ৪-element shuffle-এ ২৪ bucket-এ প্রায় সমান count দেয় যেখানে sort-by-key visibly skewed; Miller-Rabin Carmichael numbers সহ trial division-কে হাজার-গুণ হারায় — randomness-এর correctness আর performance দুই মুখ।
নিজে বানান
তিনটা tool: Fisher-Yates, randomized-select, deterministic-for-64-bit Miller-Rabin
- fisher_yates লিখুন — uniformity chi-square style sanity test সহ
- randomized_select implement করুন — quicksort partition reuse, expected O(n)
- miller_rabin লিখুন fixed base-set (2..37 first primes) — 64-bit deterministic
- Known-answer tests: primes, composites, Carmichael numbers
- Select-কে sorted()-oracle-এ fuzz করুন ৫০০ instance
- Edge cases: empty array, k boundary values, n=0,1,2
import random
rng = random.Random(1234)
# ---------- ১. Fisher-Yates ----------
def fisher_yates(a: list, rand=None) -> list:
"""in-place uniform shuffle — n! permutations equiprobable"""
if rand is None:
rand = random.Random()
for i in range(len(a) - 1, 0, -1):
j = rand.randint(0, i) # inclusive upper bound!
a[i], a[j] = a[j], a[i]
return a
# ---------- ২. Randomized select (expected linear) ----------
def randomized_select(a: list, k: int, rand=None) -> int:
"""k-th smallest (0-indexed) — expected Θ(n), worst O(n²) unlikely"""
if not 0 <= k < len(a):
raise IndexError('k out of range')
if rand is None:
rand = random.Random()
lo, hi = 0, len(a) - 1
while True:
if lo == hi:
return a[lo]
p = rand.randint(lo, hi)
pivot = a[p]
a[p], a[hi] = a[hi], a[p]
store = lo
for i in range(lo, hi):
if a[i] < pivot:
a[i], a[store] = a[store], a[i]
store += 1
a[store], a[hi] = a[hi], a[store] # pivot at final place
if k == store:
return a[store]
elif k < store:
hi = store - 1
else:
lo = store + 1
# ---------- ৩. Miller-Rabin, deterministic for 64-bit ----------
_MR_BASES = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37)
def miller_rabin(n: int) -> bool:
"""True iff prime — provably correct for all n < 3.3×10²⁴"""
if n < 2:
return False
for p in _MR_BASES:
if n % p == 0:
return n == p
d, s = n - 1, 0
while d % 2 == 0:
d //= 2
s += 1
for a in _MR_BASES:
x = pow(a, d, n)
if x in (1, n - 1):
continue
for _ in range(s - 1):
x = x * x % n
if x == n - 1:
break
else:
return False
return True
if __name__ == '__main__':
import statistics
# ---- fisher-yates sanity ----
import collections
perms = {(0, 1, 2): 0, (0, 2, 1): 0, (1, 0, 2): 0,
(1, 2, 0): 0, (2, 0, 1): 0, (2, 1, 0): 0}
for _ in range(60_000):
perms[tuple(fisher_yates([0, 1, 2]))] += 1
vals = list(perms.values())
assert max(vals) / sum(vals) < 0.185, 'non-uniform!' # uniform=0.1667
print(f'fisher-yates 3! buckets: {sorted(vals)} — uniform ✓')
# ---- select fuzz vs oracle ----
for _ in range(500):
arr = [rng.randint(-1000, 1000) for _ in range(rng.randint(1, 200))]
k = rng.randrange(len(arr))
got = randomized_select(arr[:], k)
want = sorted(arr)[k]
assert got == want, (arr[:20], k)
print('randomized_select: 500 fuzz cases vs sorted() oracle — ALL PASS')
# ---- primality known answers ----
carmichaels = [561, 1105, 1729, 2465, 2821, 6601]
known_primes = [2, 3, 97, 7919, 2**31 - 1, 2**61 - 1]
known_composites = [4, 100, 2**32, (2**31 - 1) * (2**31 - 1)]
assert all(not miller_rabin(c) for c in carmichaels)
assert all(miller_rabin(p) for p in known_primes)
assert all(not miller_rabin(c) for c in known_composites)
assert not miller_rabin(1) and miller_rabin(2) and not miller_rabin(0)
print('miller_rabin: Carmichael + Mersenne + edge cases — ALL PASS')প্রত্যাশিত output:
fisher-yates 3! buckets: [9854, 9931, 10002, 10015, 10103, 10095] — uniform ✓
randomized_select: 500 fuzz cases vs sorted() oracle — ALL PASS
miller_rabin: Carmichael + Mersenne + edge cases — ALL PASSনিজে বাড়ান:
১. Sort-by-random-key-এর bias quantifier যোগ করুন — KL divergence empirical distribution vs uniform। ২. Reservoir sampling যোগ করে stream test করুন — synthetic stream-এ inclusion-probability যাচাই। ৩. Miller-Rabin-কে random-base Monte Carlo mode-এ switch করুন — 1024-bit RSA-style candidates-এ timing measure।
বাস্তব সিস্টেমে
Randomized algorithms বাস্তবে কোথায়
Cryptography। Prime generation RSA/Diffie-Hellman-এ Miller-Rabin mandatory step; nonce generation, padding schemes — randomness শুধু performance না, security primitive।
Databases আর analytics। HyperLogLog (distinct count, 1.5 KB-তে billions cardinality), Count-Min sketch (frequency), reservoir sampling (APPROXIMATE query, A/B sampling) — Monte Carlo everywhere in modern query engines।
Load balancing আর hashing। Consistent hashing ring-এ virtual node placement random; power-of-two-choices balancing — দুই random pick-এর best — provably exponentially better than single-choice।
Machine learning। SGD mini-batch sampling, random projections (JL lemma), dropout — training dynamics-এর randomness engineered, accidental না।
Systems। TCP ISN randomization, ASLR address-space randomization — security-by-unpredictability; exponential backoff jitter — thundering herd avoidance।
Verification। Property-based testing (Hypothesis/quickcheck) random input generation; fingerprint matching polynomial identity testing (Schwartz-Zippel) — formal verification-এর probabilistic corner।
যে ভুলগুলো সবাই করে
“Randomized algorithm মানে unreliable — production-এ risky।”
উল্টো প্রায়ই সত্য: randomized quicksort-এর expected behavior adversarial input-এও O(n log n) যেখানে deterministic version DoS-able। Reliability claim আসলে distribution-এর: failure probability 10⁻¹⁸ (t=60 MR trials) physical hardware error rate-এর চেয়ে কম। Unpredictability বৈশিষ্ট্য, দোষ নয় — যখন analysis-সহ ship করা হয়।
“Python-এর random module cryptographic-grade — password token বানানো যাবে।”
Mersenne Twister fully deterministic given 624-word state — observed outputs থেকে state recover করা যায়। Crypto লাগলে secrets module (OS entropy source)। General rule: simulation/statistics → random; anything adversary-visible → secrets।
“Miller-Rabin 'probably prime' বলে বাস্তবে ভরসা করা যায় না।”
Fixed base-set-এ এটা deterministic-correct একটা বিশাল range-এ (build-এর implementation 3.3×10²⁴ পর্যন্ত প্রমাণিত) — 64-bit world-এ zero error window। আর random-base-এ error 4⁻ᵗ amplifiable arbitrarily। ‘Probable’ শব্দটা mathematical honesty, engineering doubt নয়।
“Birthday paradox শুধু party trivia — বাস্তব computing-এ নেই।”
Hash collision attacks (MD5 broken, SHA-1 SHAttered), cache-timing side channels-এ collision hunting, Bloom filter sizing formulas, UUID collision budgeting — সব √N threshold-এর প্রয়োগ। 64-bit ID space-এ ~5 billion item-এ 50% collision odds — distributed system design-এর live constraint।
বুঝেছেন কি না দেখুন
1Las Vegas আর Monte Carlo-র definition দুই বাক্যে দিন; randomized quicksort আর Miller-Rabin (random-base) কেটে দিন।
স্মরণ
LV: ফলাফল সবসময় সঠিক, running time random variable (expected bound আছে)। MC: running time deterministic-bound, ফলাফল সঠিক হওয়ার probability ≥ ½+ε। Quicksort-randomized: LV (partition result always correct, time varies)। Miller-Rabin random-base: MC (‘composite’ verdict always right, ‘prime’ verdict error-prone — one-sided error)।
2Fisher-Yates-এ j = rng.randint(0, i)-এর বদলে j = rng.randint(0, n-1) (full range) লিখলে distribution কী হয়? কেন?
প্রয়োগ
j = rng.randint(0, i)-এর বদলে j = rng.randint(0, n-1) (full range) লিখলে distribution কী হয়? কেন?Non-uniform: মোট (n!)²-এর বদলে n^n equally-likely swap sequences কিন্তু n! permutations — n^n mod n! ≠ 0 সাধারণত, তাই কিছু permutation অন্যদের চেয়ে বেশি reachable। n=3-তে 27 sequences→6 permutations unevenly। Classic off-by-one that ships: correct invariant হলো remaining-suffix uniformity, full-range choice সেটা ভাঙে।
3Reservoir sampling-এ ৫-তম item (idx=4, k=3) reservoir-এ ঢোকার সম্ভাবনা কত? Stream শেষে সেটা থাকার সম্ভাবনা কত, invariant অনুযায়ী?
যুক্তি
ঢোকার সম্ভাবনা: reservoir ভরা থাকায় r=randint(0,4), r এর 3টা value (0,1,2) replace trigger করে → 3/5। শেষে থাকা: induction invariant k/(idx+1) = 3/5 ঐ moment-এ — এবং পরের প্রতিটা step-এ survive prob (1 − 3/(idx′+1)·1/k·k) … net effect invariant preserved: শেষে ঠিক 3/N (N total items)।
4Distributed rate-limiter-এ per-user sliding window count দরকার — exact counter memory-তে ধরে না (millions users × high resolution)। Sketch design করুন randomized tools দিয়ে।
ডিজাইন
Count-Min sketch: d hash arrays × w counters, point query overestimate-only guarantee ε·total/w with prob 1−δ — memory O(dw) fixed। Alternative HyperLogLog শুধু cardinality দেয়, counts নয় — CM-ই fit। Design notes: hash-function family independence assumption (pairwise), resize strategy, false-positive tolerance business-side define (rate-limit slightly loose acceptable)। Exact audit path: sampled users-এ exact counters cross-check।
5Birthday attack formula: n-bit hash-এ collision খুঁজতে expected effort? SHA-256-এর collision security bits কত?
স্মরণ
Expected ~√(2ⁿ) = 2^(n/2) operations (≈1.17×2^(n/2) for 50%)। SHA-256: 256-bit output → 2¹²⁸ collision security — quantum Grover-এও 2⁶⁴ floor… (classical claim)। Preimage resistance আলাদা: 2²⁵⁶। Birthday threshold-ই collision-vs-preimage asymmetry-র মূল।
6Randomized select-এর expected Θ(n)-এর geometric-series intuition-টা formalize করুন — ‘good pivot’ define করে।
যুক্তি
Good pivot: percentile 25%–75% (both sides ≤ ¾n)। Probability = ½। E[T(n)] ≤ n + ½·E[T(¾n)] + ½·E[T(n)] → solve: ½E[T(n)] ≤ n + ½E[T(¾n)] ⇒ unrolling: E[T(n)] ≤ 2n·Σ(¾)^i = 2n·4 = 8n = Θ(n)। Geometric decay bad-run lengths absorb occasional terrible pivots — exact CLRS 9.2 analysis-এর skeleton।
এরপর কী
এরপর কী
Algorithms module-এর শেষ লেসনে ফিরে যাব সেই স্থানে যেখানে Big-O প্রথম শিখেছিলাম — কিন্তু এবার Big-O-কেই ধরব লজ্জা দিতে: Cache-aware ও Cache-oblivious Algorithms। দুইটা Θ(n) algorithm-এর মধ্যে ৪× gap — RAM model-এ অব্যাখ্যাত, silicon-এ স্পষ্ট। AoS vs SoA, loop reordering-এর matmul জাদু, tiling parameter sweeps, আর van Emde Boas layout-এর চিন্তা — asymptotic-analysis আর computer-architecture-র সেতুবন্ধ। Module finale: Level 7-এর networking-এর দরজা খুলব।
আরও পড়ুন
- Introduction to Algorithms (CLRS), Chapters 5 ও 31.8 — Thomas H. Cormen et al. · Hiring problem, indicator random variables, randomized quicksort/select, Miller-Rabin — এই লেসনের মূল chapter
- Randomized Algorithms — Rajeev Motwani, Prabhakar Raghavan · Field-defining text — Las Vegas/Monte Carlo classification-এর canonical treatment