135 — Miller-Rabin Intro¶
134-এ দেখলে trial division n = 10¹⁸-এ থেমে যায়; এই note-এ সেই দেয়াল ভাঙব — Miller-Rabin দিয়ে কয়েক microsecond-এ বিশাল সংখ্যার primality। মনে করিয়ে দিই: এই Level 10 competitive programming-ঘেঁষা, interview-এর জন্য optional — Miller-Rabin interview-তে প্রায় আসেই না, কিন্তু CP-তে বড়-সংখ্যা primality-র সোনার কাঠি। 134 (trial division) আর 029 (binary/modular exponentiation) ঝালাই থাকলে এটা বোঝা সহজ। এটা primality trilogy-র দ্বিতীয় ধাপ।
Source¶
- Original source: CP-Algorithms — Primality Tests (Miller-Rabin section)
- Platform: CP-Algorithms — https://cp-algorithms.com/algebra/primality_tests.html
- Topic: Math-based programming fundamentals → Level 10: Advanced Number Theory
- Difficulty: Hard
- Pattern: probabilistic test
- Basic idea inherited from: 134 (Primality Test Advanced), 029 (Binary Exponentiation)
1. Problem in simple words¶
আগের মতোই — একটা integer n prime কি না বলা। কিন্তু এবার n বিশাল (10¹⁸ পর্যন্ত), যেখানে 134-এর O(√n) trial division (~10⁹ ধাপ) অচল।
Miller-Rabin একটা চটপটে test — কয়েকটা "সাক্ষী" (base) দিয়ে n-কে জেরা করে। মূল মজা:
- Random base-এ এটা probabilistic — প্রতি জেরায় composite-কে ভুল করে prime বলার সম্ভাবনা ≤ ¼; কয়েকবার জেরা করলে সম্ভাবনা নগণ্য।
- কিন্তু একটা প্রমাণিত fact: কয়েকটা নির্দিষ্ট base-এ (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37) test করলে 64-bit পর্যন্ত (n < 3.3 × 10¹⁸) উত্তর deterministic (নিশ্চিত সঠিক)। তাই CP-তে এই base সেট-ই যথেষ্ট।
মনে রেখো — এই level CP-leaning, interview-optional; কিন্তু "সাক্ষী জেরা করে রায়" — এই probabilistic-চিন্তা গণিতগতভাবে চমৎকার।
2. What is the math idea?¶
মূল idea Fermat's little theorem-এর ধারালো রূপ। Fermat বলে: p prime হলে a^(p−1) ≡ 1 (mod p)। কিন্তু উল্টোটা সবসময় সত্য নয় — কিছু composite (Carmichael number, যেমন 561) এই test ফাঁকি দেয়।
তাই Miller-Rabin আরও সূক্ষ্ম জেরা করে। n − 1 = 2^s · d লেখো (d বিজোড়)। তারপর দেখো এই ধারা কীভাবে 1-এ পৌঁছায়:
মূল গণিত: n prime হলে, mod n-এ 1-এর বর্গমূল শুধু ±1। তাই উপরের ধারায় 1-এ পৌঁছানোর ঠিক আগের পদটা n − 1 (অর্থাৎ −1) হতেই হবে, অথবা একদম শুরুতেই a^d ≡ 1। এই দুটোর কোনোটাই না হলে — a হলো witness, n নিশ্চিত composite। এই level CP-leaning হলেও মূল সুর: "prime-এর একটা গাণিতিক স্বাক্ষর আছে; সেটা না মিললে composite ধরা পড়ে।"
3. Which basic idea does this inherit from?¶
দুটো শিকড়:
- 134 (Primality Test Advanced) — 134-এর O(√n) সীমা থেকেই Miller-Rabin-এর জন্ম; ছোট prime দিয়ে দ্রুত screening-ও 134-এর trial-division-চিন্তার ধার।
- 029 (Binary Exponentiation) — Miller-Rabin-এর কেন্দ্রে
pow(a, d, n)(modular exponentiation), যা 029-এর fast power। এটা ছাড়াa^d mod nবড় n-এ বের করা যায় না।
আর এর উপরে দাঁড়াবে 136 (Pollard Rho) — factorization-এর আগে n composite নিশ্চিত করতে Miller-Rabin লাগে (prime-এ rho অনন্ত ঘোরে)। README-র recommended order: 134 → 135 → 136।
4. Real-life analogy¶
ভাবো একটা সন্দেহভাজনকে (n) জিজ্ঞাসাবাদ করছ — সে সত্যিকারের "prime নাগরিক" না "ছদ্মবেশী composite"। তুমি কয়েকজন সাক্ষী (base a) ডাকো, প্রত্যেকে একটা নির্দিষ্ট প্রশ্ন করে।
- একজন সত্যিকারের prime নাগরিক প্রতিটা সাক্ষীর প্রশ্নে সঠিক উত্তর দেবে (গাণিতিক স্বাক্ষর মিলবে)।
- একটা ছদ্মবেশী composite হয়তো দু-একজন সাক্ষীকে বোকা বানাতে পারে — কিন্তু সাক্ষী যত বাড়াবে, ধরা পড়ার সম্ভাবনা তত বাড়ে (প্রতি সাক্ষী composite-এর অন্তত ¾ "মিথ্যা" ধরে ফেলে)।
- আর একটা প্রমাণিত fact: 64-bit-এর মধ্যে কোনো ছদ্মবেশী composite এই নির্দিষ্ট 12 জন সাক্ষীর সবাইকে একসাথে বোকা বানাতে পারে না — তাই এই সেটে সবাই পাস করলে n নিশ্চিত prime।
একজন সাক্ষী "মিথ্যা" ধরলেই (witness পেলেই) — তদন্ত শেষ, n composite।
5. Visual explanation¶
miller_rabin(561) — Carmichael number, যা Fermat-কে ফাঁকি দেয় কিন্তু Miller-Rabin ধরে:
n = 561 = 3 × 11 × 17 (composite, কিন্তু a^560 ≡ 1 অনেক a-তে — Fermat বোকা হয়)
n − 1 = 560 = 2^4 · 35, তাই s = 4, d = 35
base a = 2 দিয়ে জেরা:
x = 2^35 mod 561 = 263
263 == 1? না। 263 == 560 (n−1)? না।
বর্গ করতে থাকি (s−1 = 3 বার):
x = 263² mod 561 = 166 (== 560? না)
x = 166² mod 561 = 67 (== 560? না)
x = 67² mod 561 = 1 (== 560? না — কিন্তু 1 হয়ে গেল!)
পুরো ধারায় −1 (560) এল না, অথচ 1 এসে গেল
-> 2 হলো WITNESS -> 561 COMPOSITE ✓
তুলনায় prime — miller_rabin(97):
n−1 = 96 = 2^5 · 3, s=5, d=3
a=2: 2^3 mod 97 = 8; বর্গ: 64, 22, 96(=n−1!) -> এই base পাস
... সব base পাস করে -> 97 PRIME
লক্ষ করো — 561-এর জন্য Fermat test (2^560 mod 561) আসলে 1 দেয়, তাই Fermat বোকা হতো; কিন্তু Miller-Rabin মাঝপথে "−1 ছাড়াই 1" দেখে witness ধরে ফেলল।
6. Example input and output¶
input n -> is_prime
----------------------------------------------------------------
1 -> False
2 -> True
97 -> True
561 -> False (Carmichael — Fermat-কে ফাঁকি দেয়, MR ধরে)
1105 -> False (আরেকটা Carmichael)
1000000007 -> True (CP-র প্রিয় mod)
1000000000000000003 -> True (~10¹⁸, trial division অচল, MR মুহূর্তে)
2305843009213693951 -> True (Mersenne prime 2⁶¹ − 1)
খেয়াল করো: Carmichael number (561, 1105) — composite যারা Fermat test ফাঁকি দেয়, কিন্তু Miller-Rabin ঠিক ধরে। আর ~10¹⁸ বা Mersenne prime (2⁶¹−1)-এর মতো বিশাল সংখ্যাও Miller-Rabin তাৎক্ষণিক রায় দেয়, যেখানে 134-এর √n অচল। edge case: 1 → False, 2 → True।
7. Brute force thinking¶
"brute force" এখানে হলো 134-এর trial division — reference হিসেবে ছোট n-এ মিলিয়ে দেখতে:
def is_prime_trial(n):
if n < 2:
return False
if n < 4:
return True
if n % 2 == 0 or n % 3 == 0:
return False
i = 5
while i * i <= n:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return True
ছোট ও মাঝারি n-এ (10¹² পর্যন্ত) এটা নিখুঁত ও নির্ভরযোগ্য। তাই আমরা Miller-Rabin-এর উত্তর এই trial division-এর সাথে (ছোট range-এ) মিলিয়ে যাচাই করব — দুটো একমত হলে আস্থা বাড়ে।
8. Why brute force may be slow¶
trial division O(√n) — n বড় হলে অচল:
n = 10¹²: √n = 10⁶ ভাগ — চলে (সেকেন্ড)
n = 10¹⁸: √n = 10⁹ ভাগ — TLE (কয়েক সেকেন্ড থেকে মিনিট)
Miller-Rabin: ~12 base × O(log n) modular গুণ ≈ কয়েকশো operation — microsecond
n = 10¹⁸-এ √n = 10⁹ — একটা প্রশ্নেই TLE, আর CP-তে প্রায়ই হাজার হাজার এমন query। Miller-Rabin প্রতিটা n-এ মাত্র ~12টা base × log n modular exponentiation — তাই বিশাল n-এও তাৎক্ষণিক। এটাই O(√n) → O(log³ n)-জাতীয় লাফ।
9. Better thinking¶
মূল insight — prime-এর গাণিতিক স্বাক্ষর জেরা করো, ভাগ করার দরকার নেই। ধাপ:
1. ছোট prime (2..37) দিয়ে দ্রুত screen: n সেগুলোর একটা হলে True, ভাগ গেলে False
2. n − 1 = 2^s · d লেখো (d বিজোড় হওয়া পর্যন্ত 2 দিয়ে ভাগ)
3. প্রতিটা base a (নির্দিষ্ট সেট) জন্য:
x = pow(a, d, n) [029-এর fast modular power]
যদি x == 1 বা x == n−1: এই base পাস (continue)
নাহলে s−1 বার বর্গ করো:
x = x² mod n
যদি x == n−1: এই base পাস (break)
যদি কখনো n−1 না আসে: a witness -> return False (composite)
4. সব base পাস -> return True (prime)
কেন নির্দিষ্ট base: গণিতবিদরা প্রমাণ করেছেন {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}-এ test করলে n < 3.3 × 10¹⁸ পর্যন্ত কোনো ভুল হয় না (deterministic)। তাই random-এর ঝুঁকি ছাড়াই 64-bit-এ নিশ্চিত উত্তর। মূল ভারী কাজ pow(a, d, n) — O(log n)।
10. Thinking tweak¶
আসল "আহা" মুহূর্ত: prime modulus-এ 1-এর বর্গমূল শুধু ±1 — তাই a^d থেকে বর্গ করতে করতে 1-এ পৌঁছানোর আগে অবশ্যই −1 আসতে হবে; না এলে composite ধরা।
trial division সব ভাজক খুঁজছিল; tweak হলো — ভাগ বাদ, বরং Fermat-এর a^(n−1) ≡ 1-কে আরও সূক্ষ্ম করে "1-এর আগে −1 আসছে কি না" দেখো। এটা Carmichael-দেরও ধরে ফেলে (যাদের সাধারণ Fermat ফাঁকি দিত)। আর দ্বিতীয় tweak: নির্দিষ্ট base সেট নিলে probabilistic test 64-bit-এ deterministic হয়ে যায় — random-এর জুয়া ছাড়াই নিশ্চয়তা।
11. Step-by-step dry run¶
miller_rabin(221) — 221 = 13 × 17 (composite):
| step | কী করছি | মান |
|---|---|---|
| 1 | ছোট prime screen: 221 কি 2..37-এর কোনোটা? ভাগ যায়? | না (13, 17 > আমরা শুধু ≤37 prime দিয়ে ভাগ দেখি; 13 ভাগ করে!) |
দাঁড়াও — 221 % 13 == 0, আর 13 আমাদের screening prime সেটে আছে। তাই screening ধাপেই composite ধরা পড়ে (return False)। পরিষ্কার উদাহরণের জন্য বরং n = 25 নিই (25 = 5², কিন্তু 5 screening-এ ধরা পড়বে)...
ঠিক আছে, screening বাইপাস করে মূল Miller-Rabin লুপ দেখতে n = 561-এ base a = 2 (এর factor 3, 11, 17 সব ≤ 37 screening-এ ধরা পড়বে আসলে — তাই বাস্তবে screening-ই ধরে)। মূল লুপের গণিত বোঝাতে তাই screening ছাড়া শুধু base-জেরা দেখি, n = 561, a = 2:
| step | কী করছি | মান |
|---|---|---|
| 1 | n−1 = 560 = 2⁴·35 → s = 4, d = 35 | s=4, d=35 |
| 2 | x = pow(2, 35, 561) | 263 |
| 3 | x == 1? না। x == 560? না | চলো বর্গে |
| 4 | বর্গ 1: x = 263² % 561 = 166; == 560? না | 166 |
| 5 | বর্গ 2: x = 166² % 561 = 67; == 560? না | 67 |
| 6 | বর্গ 3: x = 67² % 561 = 1; == 560? না | 1 |
| 7 | s−1 = 3 বার বর্গ শেষ, কখনো 560 আসেনি | a=2 witness |
| 8 | return False | 561 COMPOSITE ✓ |
চূড়ান্ত: False। লক্ষ করো — ধারায় (263 → 166 → 67 → 1) কখনো −1 (560) এল না, অথচ 1 এসে গেল; এটাই composite-এর প্রমাণ (witness 2)। (বাস্তব কোডে অবশ্য 561-এর factor 3 screening-এই ধরা পড়ত — উপরের অংশটা শুধু মূল লুপের গণিত বোঝাতে।)
12. Python solution¶
def miller_rabin(n):
"""Deterministic Miller-Rabin — n < 3.3×10¹⁸ (64-bit) পর্যন্ত নিশ্চিত সঠিক।"""
if n < 2:
return False
# ছোট prime দিয়ে দ্রুত screen
small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
for p in small_primes:
if n % p == 0:
return n == p
# n − 1 = 2^s · d (d বিজোড়)
d, s = n - 1, 0
while d % 2 == 0:
d //= 2
s += 1
# নির্দিষ্ট base — এই সেটে 64-bit পর্যন্ত deterministic
for a in small_primes:
x = pow(a, d, n) # 029-এর fast modular power
if x == 1 or x == n - 1:
continue # এই base পাস
for _ in range(s - 1):
x = x * x % n
if x == n - 1:
break
else:
return False # a witness -> composite
return True
# --- ছোট test গুলো (সব pass করা উচিত) ---
def is_prime_trial(n):
if n < 2:
return False
if n < 4:
return True
if n % 2 == 0 or n % 3 == 0:
return False
i = 5
while i * i <= n:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return True
# Miller-Rabin বনাম trial division — 0..5000 সব মেলাও
for n in range(0, 5001):
assert miller_rabin(n) == is_prime_trial(n), f"MR({n}) অমিল"
# Carmichael numbers: composite, Fermat-কে ফাঁকি দেয় — MR ধরবে
for c in [561, 1105, 1729, 2465, 2821, 6601, 8911]:
assert miller_rabin(c) is False, f"Carmichael {c} ভুল"
# জানা বড় prime
assert miller_rabin(1000000007) is True
assert miller_rabin(1000000009) is True
assert miller_rabin(999999937) is True # 10⁹-এর নিচে বড় prime
# বিশাল সংখ্যা — trial division অচল, MR তাৎক্ষণিক
assert miller_rabin(1000000000000000003) is True # ~10¹⁸ prime
assert miller_rabin(2305843009213693951) is True # Mersenne 2⁶¹ − 1, prime
assert miller_rabin(1000000000000000000) is False # 10¹⁸ = 2^18·5^18, composite
assert miller_rabin(9223372036854775783) is True # 64-bit-এর কাছাকাছি বড় prime
# trial division-এর সাথে কিছু বড় composite-ও মেলাও
assert miller_rabin(1000003 * 1000033) is False # দুই prime-এর গুণফল
print(miller_rabin(97)) # True
print(miller_rabin(561)) # False (Carmichael)
print(miller_rabin(1000000000000000003)) # True (~10¹⁸)
print("সব test pass!")
13. Line-by-line explanation¶
small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
for p in small_primes:
if n % p == 0:
return n == p
দ্রুত screening — n যদি এই ছোট prime-গুলোর গুণিতক হয়, তবে n composite (যদি না n নিজেই সেই prime)। এটা ছোট composite দ্রুত ফেলে দেয়, আর n = এই prime-গুলো হলে সরাসরি True।
n − 1 = 2^s · d decompose — d বিজোড় না হওয়া পর্যন্ত 2 দিয়ে ভাগ, প্রতিবার s বাড়াই। এই s আর d-ই Miller-Rabin-এর ধারার ভিত্তি।
প্রতিটা base a-এর জন্য a^d mod n (fast power)। শুরুতেই 1 বা n−1 (−1) এলে এই base prime-এর সাথে সঙ্গতিপূর্ণ — পরের base-এ যাই।
নাহলে s−1 বার বর্গ করি; কোথাও n−1 (−1) এলে এই base পাস (break, else চলে না)। কিন্তু পুরো লুপে n−1 না এলে (for-else-এর else চলে) — a হলো witness, n composite।
সব base পাস করলে n prime (64-bit-এ deterministic)।
বাকি test অংশে Miller-Rabin বনাম trial division (0..5000), Carmichael সেট (561, 1105, ...), জানা বড় prime, আর ~10¹⁸/Mersenne/64-bit-কাছাকাছি বিশাল সংখ্যা সব যাচাই হয়। সব ঠিক থাকলে সব test pass!।
14. Output walkthrough¶
চালালে যা ছাপবে:
miller_rabin(97) → True (prime, সব base পাস)। miller_rabin(561) → False (Carmichael — Fermat ফাঁকি দিত, কিন্তু এখানে factor 3 screening-এ ধরা)। miller_rabin(1000000000000000003) → True (~10¹⁸ prime, তাৎক্ষণিক — trial division হলে 10⁹ ভাগ লাগত)। তার আগে 0..5000 trial-এর সাথে মিল, সব Carmichael ও বিশাল সংখ্যা pass — তাই সব test pass!।
15. Time complexity¶
O(k · log³ n) মোটামুটি — যেখানে k = base সংখ্যা (এখানে 12, ধ্রুবক)। প্রতিটা base-এ একটা pow(a, d, n) (O(log n) modular গুণ), আর প্রতিটা modular গুণ বড় সংখ্যায় ~O(log² n)। তাই কার্যত n-এর bit-সংখ্যার polynomial — n = 10¹⁸-এও কয়েকশো operation, microsecond। trial division-এর O(√n)-এর তুলনায় বিশাল লাফ।
16. Space complexity¶
O(1) — base array ধ্রুব আকারের (12টা), আর গুটিকয় variable (d, s, x)। কোনো n-নির্ভর array নেই। pow-ও in-place modular exponentiation, extra space নগণ্য। তাই বিশাল n-এও memory ধ্রুব।
17. Common mistakes¶
n − 1 = 2^s · ddecompose-এ off-by-one — d বিজোড় না হওয়া পর্যন্ত 2 দিয়ে ভাগ; s ভুল গুনলে ভুল রায়। যাচাই: শেষে2^s · d == n − 1হওয়া উচিত।- for-else ভুল বোঝা — ভেতরের
for ... elseমানে "লুপ break ছাড়া শেষ হলে else চলে"; এখানে n−1 না এলে witness।else-কেif-এর সাথে গুলিয়ে ফেলো না। - base-এ n নিজেই থাকলে — n ছোট prime (যেমন 3) হলে screening-এ
n == p-তে True; না সামলালে base a = n হয়েpow(n, d, n) == 0, ভুল। screening আগে রাখলে নিরাপদ। - ভুল base সেট/range — {2,...,37} শুধু n < 3.3×10¹⁸-এ deterministic; এর বেশি বড় n-এ আরও base বা random লাগবে।
pow(a, d, n)-এর বদলে নিজে ধীর exponentiation — Python-এর built-inpow(a, d, n)fast modular power; loop-এa**d % nলিখলে বিশাল সংখ্যায় অসম্ভব ধীর।
18. Harder problems that inherit this idea¶
- CSES — Counting Primes / প্রচুর বড়-n primality query (https://cses.fi/problemset/) — যেখানে trial division অচল।
- Codeforces — বড় n factorization/primality problems (https://codeforces.com/problemset) — Miller-Rabin দিয়ে n prime কি না, তারপর Pollard rho দিয়ে factor।
- 136 (Pollard Rho Intro) — এই repo-রই পরের ধাপ; rho চালানোর আগে Miller-Rabin দিয়ে composite নিশ্চিত করতে হয়। ভিত্তি 134, 029।
19. Pattern learned¶
Probabilistic primality via Miller-Rabin — n−1 = 2^s·d, প্রতিটা base a-তে a^d, a^(2d), ... ধারায় "1-এর আগে −1" খোঁজো; না পেলে composite। নির্দিষ্ট 12 base-এ 64-bit deterministic। বড় শিক্ষা: বড় n-এ "ভাগ করে খোঁজা" বাদ দিয়ে prime-এর গাণিতিক স্বাক্ষর (square-root of unity) যাচাই করো — তখন O(√n) থেকে O(log³ n)-এ নামা যায়।
20. Final summary¶
ভবিষ্যতের আমাকে এক লাইনে: "Miller-Rabin: n−1 = 2^s·d; base a-তে x = a^d, বর্গ করতে করতে n−1 খোঁজো; না পেলে witness → composite; base {2..37}-এ n < 3.3e18 deterministic; ভারী কাজ pow(a,d,n)।" Signal: "বিশাল n prime কি না" বা "বড় n factorize-এর আগে primality" — দেখলেই Miller-Rabin মনে পড়বে।
পরের ধাপ → 136 — Pollard Rho Intro (বড় composite-এর factor শিকার)। ভিত্তি → 134 — Primality Test Advanced, 029 (Binary Exponentiation)।
ফিরে দেখা: এই level-এর map → ../README.md · concept ঝালাই → ../concept-notes.md · note-এর ছক → ../../../templates/math-problem-note-template.md।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" এমন দাবি করা হয়নি — এই level CP-leaning, interview-optional।