Foundationপ্রথম নীতি থেকে
LEVEL 6লেসন ২৩/২৪অ্যাডভান্সড৫০ মিনিট

Randomized Algorithms — dice নিয়ে correctness

Randomized Algorithms

Randomness শুধু speed নয় — adversary-কে অস্ত্রহীন করে: Las Vegas সঠিক উত্তর random সময়ে, Monte Carlo bounded সময়ে probable উত্তর; দুটোই মাপা যায়।

এই লেসন শেষে আপনি পারবেন

  • Las Vegas আর Monte Carlo-র চুক্তি পার্থক্য করে বলতে পারবেন — কোন algorithm কোন class, trade-off কোথায়
  • Fisher-Yates shuffle-এর uniformity sketch দিয়ে ব্যাখ্যা করতে পারবেন, আর sort-by-random-key biased shuffle bug-টা counterexample-সহ ধরতে পারবেন
  • Randomized quicksort-র expected O(n log n)-এর adversary-defeat argument বলতে পারবেন — input order irrelevant হয় কীভাবে
  • Birthday paradox থেকে hash birthday attack-এর গণিত derive করে ~sqrt(n) collision threshold হিসাব করতে পারবেন
  • Miller-Rabin-এর witness logic বুঝিয়ে fixed base-set-এর deterministic-for-64-bit claim প্রয়োগ করতে পারবেন আর reservoir sampling-এর streaming invariant proof-sketch দিতে পারবেন

আগে যা বোঝা থাকা দরকার

আগে এটা বুঝি

গত লেসনে 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 VegasMonte Carlo
সঠিকতা১০০% guaranteedp ≥ ½+ε (amplifiable)
সময়random variable, expected bounddeterministic bound
উদাহরণrandomized quicksort, selectMiller-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 a

Correctness 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 reservoir

Invariant: 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 witness

Composite-এর অন্তত ¾ 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 ধরা পড়ল।
Miller-Rabin trace: n=561 (Carmichael), base a=2 — Fermat passes কিন্তু square-root structure ধরা পড়ে।

উদাহরণ

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

Dice-এর দুই চুক্তি আর তাদের নাগাল
  1. Las Vegascorrect always, time random — quicksort, select, Fisher-Yates
  2. Monte Carlotime bounded, error probabilistic — Miller-Rabin, Karger cut
  3. Amplification bridgerepetition t times: MC error 2⁻ᵗ; LV cutoff → MC conversion
  4. 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
Adversary model — deterministic algorithm-এ input worst-case বেছে নেয়; randomized-এ input আর execution decouple, adversary blindfolded।

নিজে চালিয়ে দেখুন

EXPERIMENT

Shuffle uniformity + primality race — dice-এর fairness আর speed, মাপা

macOS / Linux (Python 3.10+)· ২০ মিনিট
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 দুই মুখ।

নিজে বানান

BUILD IT

তিনটা tool: Fisher-Yates, randomized-select, deterministic-for-64-bit Miller-Rabin

Python · ●●●○○
  1. fisher_yates লিখুন — uniformity chi-square style sanity test সহ
  2. randomized_select implement করুন — quicksort partition reuse, expected O(n)
  3. miller_rabin লিখুন fixed base-set (2..37 first primes) — 64-bit deterministic
  4. Known-answer tests: primes, composites, Carmichael numbers
  5. Select-কে sorted()-oracle-এ fuzz করুন ৫০০ instance
  6. 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।

বুঝেছেন কি না দেখুন

1

Las 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)।

2

Fisher-Yates-এ 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 সেটা ভাঙে।

3

Reservoir 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)।

4

Distributed 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।

5

Birthday 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-র মূল।

6

Randomized 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