এই পর্বে যা শিখবেন
- trial division কেন √n-এ থামে, আর sieve-এর O(n log log n) কোথা থেকে আসে
- linear sieve দিয়ে smallest prime factor টেবিল বানিয়ে লাখো সংখ্যা মুহূর্তে উৎপাদকে ভাঙা
- Euclid-এর gcd-এর প্রমাণ, Lamé-এর সীমা, আর extended Euclid হাতে চালানো
- modular inverse দুইভাবে, nCr mod 10⁹ + 7, Euler-এর φ আর Chinese remainder theorem
- Fermat-এর পরীক্ষা কেন ঠকে, আর Miller–Rabin কীভাবে 64-bit সংখ্যায় নির্ভুল
১ · বিভাজ্যতা আর মৌলিক সংখ্যা — √n পর্যন্ত ভাগই যথেষ্ট
পরীক্ষার হলে ৩৬ জন পরীক্ষার্থীকে সমান সারিতে বসাতে হবে। ৪ সারিতে ৯ জন, ৬ সারিতে ৬ জন, ৯ সারিতে ৪ জন — হয়। কিন্তু ৩৭ জন হলে? ১ সারি বা ৩৭ সারি ছাড়া কোনো উপায় নেই। ৩৭ একটি মৌলিক সংখ্যা (prime). Number theory বা সংখ্যাতত্ত্ব পূর্ণসংখ্যার এমন ধর্ম নিয়ে কাজ করে, আর অ্যালগরিদমে এর প্রয়োগ সর্বত্র: hash-এর mod (পর্ব ৩২), কনটেস্টের “উত্তর 10⁹ + 7 দিয়ে ভাগশেষ”, আর ইন্টারনেটের নিরাপত্তা।
ভাষাটা আগে ঠিক করি। a | b মানে a দিয়ে b নিঃশেষে বিভাজ্য (b = a·k). ১-এর বড় যে সংখ্যার উৎপাদক শুধু ১ আর সে নিজে, সেটি মৌলিক; বাকিরা যৌগিক (composite). Fundamental theorem of arithmetic বলে, প্রতিটি সংখ্যা মৌলিকের গুণফলে ঠিক একভাবে ভাঙে: 360 = 2³·3²·5. মৌলিকরা তাই সংখ্যার “ইট”।
Trial division আর √n-এর যুক্তি
n মৌলিক কি না জানতে 2 থেকে n − 1 পর্যন্ত সব দিয়ে ভাগ করার দরকার নেই। যদি n = a·b আর a ≤ b হয়, তাহলে a·a ≤ a·b = n, অর্থাৎ a ≤ √n. তাই যৌগিক n-এর অন্তত একটি উৎপাদক √n-এর সমান বা ছোট — সেখানে কেউ ভাগ না করলে n মৌলিক। এটিই trial division, সময় O(√n). 36-এর উৎপাদক-জোড়া (1, 36), (2, 18), (3, 12), (4, 9), (6, 6) — প্রতিটি জোড়ার ছোটটি ≤ 6 = √36.
Pseudocode
IS-PRIME(n):
if n < 2: return false
for d = 2 while d * d <= n: // d*d avoids floating-point sqrt
if n mod d == 0: return false
return trueO(√n) “দ্রুত” শোনালেও ইনপুটের আকারে তা exponential. n লিখতে লাগে b = log₂ n bit, আর √n = 2^(b/2). 10¹² পর্যন্ত 10⁶ ভাগ — চলে; কিন্তু 600 অঙ্কের সংখ্যায় এটি মহাবিশ্বের আয়ুতেও শেষ হবে না। এ ধরনের সময়কে বলে pseudo-polynomial. ঠিক এই ফাঁক থেকেই আধুনিক ক্রিপ্টোগ্রাফির জন্ম (অংশ ৮-এর শেষে)।
মৌলিক কতগুলো? Euclid-এর Elements-এর (আনুমানিক খ্রিস্টপূর্ব ৩০০) প্রমাণ — proof by contradiction: ধরুন সসীম, p₁, …, pₖ. N = p₁·p₂·…·pₖ + 1-কে কোনো pᵢ ভাগ করলে ভাগশেষ 1, অথচ N-এর একটি মৌলিক উৎপাদক থাকতেই হবে — যা তালিকায় নেই। স্ববিরোধ। আর তারা কত ঘন? Prime number theorem বলে n পর্যন্ত মৌলিক প্রায় n / ln n টি: 10⁶ পর্যন্ত আসলে 78498টি, সূত্রে ≈ 72382 — কাছাকাছি, আর n বড় হলে অনুপাত 1-এর দিকে যায়।
২ · Sieve of Eratosthenes — একবারে সব মৌলিক
10⁶ পর্যন্ত প্রতিটি সংখ্যায় আলাদা করে trial division চালালে মোট কাজ মোটামুটি n√n / ln n-এর ক্রমে — অনেক। উল্টো পথে ভাবুন: মৌলিক খোঁজার বদলে যৌগিকগুলো কেটে দিন। চালনিতে আটা চালার মতো — গুণিতকরা ঝরে পড়ে, মৌলিকরা থেকে যায়। এটিই sieve of Eratosthenes: 2 থেকে n লিখুন; সবচেয়ে ছোট না-কাটা সংখ্যা p মৌলিক; তার গুণিতকগুলো p² থেকে কাটুন (2p, 3p, … (p−1)p আগেই ছোট মৌলিকের হাতে কাটা পড়েছে); p² > n হলে থামুন। n = 30:
| বর্তমান মৌলিক p | |
| কাটা শুরু (p²) | |
| এই ধাপে কাটার চেষ্টা | |
| তার মধ্যে নতুন কাটা | |
| না-কাটা বাকি (2…30) |
- শুরু। 2 থেকে 30 — ২৯টি প্রার্থী, কেউ কাটা নয়। 1 মৌলিকও নয়, যৌগিকও নয়, তাই বাদ।
- p = 2. সবচেয়ে ছোট না-কাটা সংখ্যা 2 — মৌলিক। 4 থেকে দুই ঘর পরপর ১৪টি জোড় সংখ্যা কাটা; বাকি ১৫টি।
- p = 3. পরের না-কাটা 3. কাটা শুরু 9 থেকে, কারণ 6 আগেই 2-এর হাতে কাটা। ৮টি চেষ্টার মধ্যে 12, 18, 24, 30 আবার কাটা — নতুন শুধু ৪টি।
- p = 5. শুরু 25: 25 নতুন, 30 তৃতীয়বার! এই বারবার কাটাই sieve-এর অতিরিক্ত খরচ — অংশ ৩-এর linear sieve এটি দূর করে।
- থামা। পরের মৌলিক 7, কিন্তু 49 > 30 — 30-এর ভেতরের প্রতিটি যৌগিকের একটি উৎপাদক ≤ √30 ≈ 5.5, তাই সবাই কাটা হয়ে গেছে। বাকি ১০টি মৌলিক; ২৪টি চেষ্টায় ১৯টি যৌগিক কাটা।
O(n log log n) কেন — একটি স্কেচ
মৌলিক p-এর জন্য কাটার চেষ্টা প্রায় n/p. মোট কাজ n·(1/2 + 1/3 + 1/5 + 1/7 + …), শুধু মৌলিক p ≤ n-এর ওপর। সব k-এর ওপর 1/k-এর যোগফল harmonic series, ≈ ln n (পর্ব ০৬). মৌলিকরা অনেক বিরল — k-তম মৌলিক প্রায় k ln k — তাই তাদের উল্টোর যোগফল অনেক ধীরে বাড়ে: Mertens-এর (১৮৭৪) ফল অনুযায়ী ∑ 1/p ≈ ln ln n + 0.26. সুতরাং:
ln ln 10⁶ ≈ 2.6, অর্থাৎ বাস্তবে এটি প্রায় রৈখিক। লুপের শর্ত p * p <= n-এ n ≈ 2 × 10⁹ হলে int-এ গুণফল overflow করে — long long দিন বা p <= n / p লিখুন। জায়গা বাঁচাতে C++-এ vector<bool> বা bitset (প্রতি সংখ্যায় এক bit), আর শুধু বিজোড় সংখ্যা রাখলে আরও অর্ধেক।
Segmented sieve — বড় পরিসরের জন্য। প্রশ্ন: [10¹² − 10⁶, 10¹²]-এর ভেতরের মৌলিকগুলো বের করুন। 10¹² ঘরের array অসম্ভব। কিন্তু ওই জানালার প্রতিটি যৌগিকের একটি উৎপাদক ≤ √10¹² = 10⁶. তাই আগে সাধারণ sieve-এ 10⁶ পর্যন্ত মৌলিক বের করুন, তারপর শুধু জানালার 10⁶ + 1 ঘরে তাদের গুণিতক কাটুন (প্রথম গুণিতক ⌈L/p⌉·p). এটিই segmented sieve: জায়গা O(√R + (R − L)). বড় n-এর সাধারণ sieve-ও টুকরো টুকরো (cache-এ আঁটে এমন মাপে) চালালে বাস্তবে কয়েক গুণ দ্রুত হয়।
৩ · Linear sieve আর smallest prime factor — উৎপাদকে ভাঙা O(log n)-এ
stepper-এ 30 কাটা হয়েছে তিনবার (2, 3, 5 — তিনজনই তার উৎপাদক)। Linear sieve (Gries ও Misra, ১৯৭৮) প্রতিটি যৌগিক x-কে কাটে ঠিক একবার — তার smallest prime factor spf[x] দিয়ে। নিয়ম: প্রতিটি i-এর জন্য এ পর্যন্ত পাওয়া মৌলিক p = 2, 3, 5, … নিয়ে spf[i·p] = p লিখুন, কিন্তু p > spf[i] হলেই থামুন।
কেন ঠিক একবার? যেকোনো যৌগিক x-কে একভাবেই x = i·p লেখা যায় যেখানে p = spf[x] আর i = x / p; তখন i-এর সব উৎপাদক ≥ p, তাই p ≤ spf[i] — শর্ত মানে, x লেখা হয় i-এর ধাপে। অন্য কোনো জোড়া (i′, p′) যেখানে p′ > spf[x], সেখানে spf[i′] = spf[x] < p′, তাই লুপ আগেই থেমে যায়। ফলে মোট লেখা = যৌগিকের সংখ্যা, সময় Θ(n), জায়গা Θ(n) int (spf টেবিল)।
spf টেবিলের আসল লাভ: x-কে উৎপাদকে ভাঙতে বারবার p = spf[x] দিয়ে ভাগ করুন। প্রতিটি ভাগে x অন্তত অর্ধেক হয়, তাই ধাপ ≤ log₂ x — 10⁵টি প্রশ্নেও মুহূর্তের কাজ, যেখানে প্রতিটিতে trial division হলে O(√x). ছোট উদাহরণ: 2 থেকে 12-এর spf = 2, 3, 2, 5, 2, 7, 2, 3, 2, 11, 2. তাই 12 → (÷2) 6 → (÷2) 3 → (÷3) 1, অর্থাৎ 12 = 2²·3 — তিনটি ভাগ, কোনো খোঁজাখুঁজি নেই।
#include <initializer_list>
#include <iostream>
#include <vector>
using namespace std;
int main() {
const int N = 1000000;
vector<int> spf(N + 1, 0), primes; // spf[x] = smallest prime factor of x
long long marks = 0; // how many times a composite is written
for (int i = 2; i <= N; ++i) {
if (spf[i] == 0) { spf[i] = i; primes.push_back(i); } // nobody marked i: prime
for (int p : primes) { // x = i * p gets spf[x] = p
if (p > spf[i] || (long long)i * p > N) break;
spf[i * p] = p;
++marks;
}
}
cout << "primes <= " << N << ": " << primes.size() << ", largest " << primes.back() << '\n';
cout << "composites marked: " << marks << " (= " << N - 1 - (long long)primes.size() << " composites)\n";
for (int x : {360, 2026, 997920, 999983}) {
cout << x << " =";
for (char sep = ' '; x > 1; sep = '*') { // O(log x): each step divides by >= 2
int p = spf[x], e = 0;
while (x % p == 0) { x /= p; ++e; }
cout << sep << p;
if (e > 1) cout << '^' << e;
}
cout << '\n';
}
}আউটপুট:
primes <= 1000000: 78498, largest 999983
composites marked: 921501 (= 921501 composites)
360 = 2^3*3^2*5
2026 = 2*1013
997920 = 2^5*3^4*5*7*11
999983 = 999983
10⁶ পর্যন্ত মৌলিক 78498টি — সুপরিচিত সংখ্যা, নিজের sieve যাচাইয়ের প্রথম পরীক্ষা হিসেবে মনে রাখুন। দ্বিতীয় লাইনটিই linear sieve-এর প্রমাণের হাতে-কলমে রূপ: লেখা হয়েছে ঠিক 10⁶ − 1 − 78498 = 921501 বার, প্রতিটি যৌগিক একবার। (long long)i * p জরুরি — i ≈ 10⁶ আর p বড় হলে int-এ গুণফল overflow করে। 999983 নিজের spf, তাই মৌলিক — 10⁶-এর নিচের সবচেয়ে বড়টি।
N = 10**6
spf = [0] * (N + 1) # spf[x] = smallest prime factor of x
primes, marks = [], 0 # marks = how many times a composite is written
for i in range(2, N + 1):
if spf[i] == 0: # nobody marked i: prime
spf[i] = i
primes.append(i)
for p in primes: # x = i * p gets spf[x] = p
if p > spf[i] or i * p > N:
break
spf[i * p] = p
marks += 1
print(f"primes <= {N}: {len(primes)}, largest {primes[-1]}")
print(f"composites marked: {marks} (= {N - 1 - len(primes)} composites)")
for x in (360, 2026, 997920, 999983):
parts, y = [], x
while y > 1: # O(log x): each step divides by >= 2
p, e = spf[y], 0
while y % p == 0:
y, e = y // p, e + 1
parts.append(f"{p}^{e}" if e > 1 else str(p))
print(f"{x} = " + "*".join(parts))আউটপুট C++-এর সাথে হুবহু এক:
primes <= 1000000: 78498, largest 999983
composites marked: 921501 (= 921501 composites)
360 = 2^3*3^2*5
2026 = 2*1013
997920 = 2^5*3^4*5*7*11
999983 = 999983
Python-এ প্রায় দশ লাখ বার ভেতরের লুপ চলে, তাই C++-এর চেয়ে কয়েক গুণ ধীর (তবু এক সেকেন্ডের আশেপাশে)। শুধু মৌলিকের তালিকা লাগলে Python-এ সাধারণ sieve-ই দ্রুত, কারণ is_p[p*p::p] = ... ধরনের slice-assignment ভেতরে C-তে চলে; spf লাগলে linear sieve. Python-এর int-এ overflow নেই, তাই i * p-তে কোনো cast লাগেনি।
৪ · Euclid-এর GCD — প্রমাণ, Lamé-এর সীমা আর LCM
252 সেমি × 198 সেমি একটি ঘরের মেঝেতে সবচেয়ে বড় কোন মাপের বর্গাকার টাইল বসালে কাটতে হবে না? উত্তর দুই দৈর্ঘ্যের gcd — গসাগু। স্কুলে শেখা “দুটিকেই উৎপাদকে ভাঙো” পদ্ধতি বড় সংখ্যায় অচল, কারণ উৎপাদকে ভাঙাই কঠিন। Euclid-এর অ্যালগরিদম উৎপাদক ছাড়াই কাজ করে: মেঝে থেকে যত বড় সম্ভব বর্গ কেটে নিন, বাকি আয়তক্ষেত্রে একই কাজ করুন।
b > 0 হলে gcd(a, b) = gcd(b, a mod b), আর gcd(a, 0) = a.
প্রমাণ। a = q·b + r লিখুন, r = a mod b. d যদি a আর b দুটিকেই ভাগ করে, তাহলে r = a − q·b-কেও ভাগ করে। উল্টো দিকে d যদি b আর r-কে ভাগ করে, তাহলে a = q·b + r-কেও ভাগ করে। অর্থাৎ (a, b) আর (b, r)-এর সাধারণ উৎপাদকের সেট হুবহু এক — তাই সবচেয়ে বড়টিও এক। প্রতিটি ধাপে দ্বিতীয় সংখ্যা কঠোরভাবে কমে (r < b), তাই অ্যালগরিদম শেষ হয়।
| (a, b) | |
| a = q·b + r | |
| পরের জোড়া | |
| ফল |
- ধাপ ১. 252 × 198 থেকে একটি 198 × 198 বর্গ কাটা যায় (q = 1), বাকি 54 × 198. সংখ্যায়: 252 mod 198 = 54, নতুন জোড়া (198, 54).
- ধাপ ২. 198 × 54 থেকে তিনটি 54-এর বর্গ (q = 3), বাকি 36: 198 = 3·54 + 36. একবারে তিনটি — বারবার বিয়োগের বদলে mod নেওয়ার সুবিধা এখানেই।
- ধাপ ৩. 54 = 1·36 + 18, জোড়া (36, 18).
- ধাপ ৪. 36 = 2·18 + 0 — ঠিক দুটি বর্গ, ভাগশেষ শূন্য। এর মানে 18 আগের সব টুকরোকে নিঃশেষে ভাগ করে, তাই মূল মেঝেকেও।
- ফল। gcd(252, 198) = 18: ১৫৪টি 18 সেমির টাইল। মাত্র ৪টি ভাগ। lcm = 252 / 18 · 198 = 14 · 198 = 2772.
কত ধাপ? O(log min(a, b)) আর Lamé
মূল পর্যবেক্ষণ: a ≥ b হলে a mod b < a/2. কারণ b ≤ a/2 হলে ভাগশেষ < b ≤ a/2; আর b > a/2 হলে a mod b = a − b < a/2. জোড়া (a, b) → (b, r) → (r, …), তাই দুই ধাপে প্রথম সংখ্যা অন্তত অর্ধেক — মোট ধাপ ≤ 2 log₂ min(a, b) + 1, অর্থাৎ O(log min(a, b)) ভাগ। 10¹⁸-এর মতো সংখ্যাতেও ১০০-এর কম ধাপ।
সবচেয়ে খারাপ ইনপুট কোনটি? প্রতিটি ধাপে q = 1 হলে সংখ্যা সবচেয়ে ধীরে কমে — ঠিক পরপর দুটি Fibonacci সংখ্যা: gcd(89, 55) → (55, 34) → (34, 21) → … → (1, 0), ৯টি ধাপ। Lamé ১৮৪৪ সালে এটি প্রমাণ করেন: ধাপের সংখ্যা ছোট সংখ্যার দশমিক অঙ্কের ৫ গুণের বেশি নয়। অনেকে একে কোনো অ্যালগরিদমের সময়-বিশ্লেষণের প্রথম দিকের উদাহরণ বলেন।
LCM আর overflow — আগে ভাগ, পরে গুণ
lcm(a, b) · gcd(a, b) = a · b, তাই lcm = a·b / gcd. কিন্তু C++-এ a * b / g লিখলে a, b ≈ 10¹² হলে গুণফল 10²⁴ — long long (≈ 9.2 × 10¹⁸) ছাড়িয়ে যায়, যদিও উত্তর হয়তো ছোট। সঠিক ক্রম a / g * b: g দিয়ে a নিঃশেষে বিভাজ্য, তাই ভাগে কিছু হারায় না। C++17-এর std::gcd, std::lcm (<numeric>) আর Python-এর math.gcd, math.lcm তৈরি আছে; অনেক সংখ্যার gcd = gcd(gcd(a, b), c) … দৈনন্দিন একটি ব্যবহার: 1920 × 1080 পর্দার অনুপাত — gcd = 120, তাই 16 : 9.
৫ · Extended Euclid — Bézout-এর x আর y
Euclid-এর ধাপগুলো উল্টো দিকে জুড়ে দিলে আরও একটি তথ্য পাওয়া যায়। Bézout's identity: যেকোনো a, b-এর জন্য এমন পূর্ণসংখ্যা x, y আছে যাতে
Extended Euclidean algorithm এই x, y বের করে। recursive রূপের যুক্তি: gcd(b, a mod b)-এর জন্য b·x′ + (a mod b)·y′ = g জানা থাকলে, a mod b = a − ⌊a/b⌋·b বসিয়ে সাজালে a·y′ + b·(x′ − ⌊a/b⌋·y′) = g. তাই x = y′, y = x′ − ⌊a/b⌋·y′; ভিত্তি b = 0-এ (x, y) = (1, 0). Iterative রূপে প্রতিটি সারিতে একটি সংখ্যা r আর তার জোড়া (x, y) রাখি, এই invariant ধরে: r = a·x + b·y. প্রতিটি নতুন সারি = আগের-আগের সারি − q × আগের সারি, তাই invariant টিকে থাকে। নিচে a = 3120, b = 17 (এই সংখ্যাগুলো অংশ ৮-এর খেলনা RSA-তে আবার আসবে):
| q = ⌊আগের-আগের r / আগের r⌋ | |
| নতুন r | |
| নতুন x | |
| নতুন y | |
| যাচাই |
- শুরুর দুই সারি। 3120 = 3120·1 + 17·0 আর 17 = 3120·0 + 17·1 — invariant নিজে থেকেই সত্য।
- q = 183. নতুন সারি = প্রথম − 183 × দ্বিতীয়: r = 9, x = 1, y = −183. সাধারণ Euclid-এর মতো ভাগশেষ 9, শুধু সাথে (x, y)-ও হিসাব হলো।
- q = 1. দ্বিতীয় − 1 × তৃতীয়: r = 8, (x, y) = (−1, 184). যাচাই: −3120 + 3128 = 8.
- q = 1. r = 1, (x, y) = (2, −367). r = 1 মানে gcd 1 — 3120 আর 17 সহমৌলিক (coprime).
- শেষ ধাপ আর inverse. পরের ভাগশেষ 0, তাই শেষ অশূন্য r = 1 = gcd. দুই পাশে mod 3120 নিলে 17·(−367) ≡ 1, অর্থাৎ 17-এর inverse −367 ≡ 2753 (mod 3120). ৪টি ভাগে কাজ শেষ — Euclid-এর মতোই O(log min(a, b)).
প্রয়োগ — linear Diophantine equation: a·x + b·y = c-এর পূর্ণসংখ্যা সমাধান থাকে ঠিক তখন, যখন g = gcd(a, b) দিয়ে c বিভাজ্য। একটি সমাধান (x·c/g, y·c/g), আর সব সমাধান x + k·b/g, y − k·a/g. ধরুন ৭ টাকা আর ৫ টাকার স্ট্যাম্প দিয়ে ঠিক ৩১ টাকা বানাতে হবে: gcd = 1 | 31, তাই সমাধান আছে — যেমন 3·7 + 2·5 = 31.
৬ · Modular arithmetic — ভাগশেষের হিসাব, দ্রুত ঘাত আর inverse
আজ সোমবার। ১০০ দিন পর কী বার? ১০০ দিন গোনার দরকার নেই: 100 mod 7 = 2, তাই সোমবার + ২ = বুধবার। সপ্তাহের বার, ঘড়ির ঘণ্টা — এগুলো modular arithmetic: একটি সীমা m-এ পৌঁছে আবার শূন্য থেকে। লেখা হয় a ≡ b (mod m), মানে m | (a − b).
| কাজ | নিয়ম | সতর্কতা |
|---|---|---|
| যোগ | (a + b) mod m = ((a mod m) + (b mod m)) mod m | দুটি < m যোগ করলে < 2m |
| বিয়োগ | (a − b) mod m = ((a − b) mod m + m) mod m | C++-এ % ঋণাত্মক ফল দিতে পারে; Python-এ নয় |
| গুণ | (a · b) mod m = ((a mod m)·(b mod m)) mod m | m ≈ 10⁹ হলে গুণফল ≈ 10¹⁸ — long long লাগে |
| ভাগ | a / b ≡ a · b⁻¹ (mod m), যদি gcd(b, m) = 1 | (a mod m) / (b mod m) সাধারণত ভুল |
কেন মাঝপথে mod নেওয়া নিরাপদ? a = q₁·m + r₁ আর b = q₂·m + r₂ লিখুন। তাহলে a + b = (q₁ + q₂)·m + (r₁ + r₂) আর a·b = (q₁q₂m + q₁r₂ + q₂r₁)·m + r₁·r₂ — m-এর গুণিতক অংশ ভাগশেষে কোনো প্রভাব ফেলে না, তাই শুধু r₁, r₂ নিয়ে হিসাব করলেই চলে। ভাগে এটি ভাঙে: 12 / 4 = 3, অথচ mod 8-এ 12 ≡ 4, আর 4 / 4 = 1 ≠ 3. এ কারণেই ভাগের জন্য inverse লাগে।
কনটেস্টে উত্তর কেন “mod 10⁹ + 7”
“কতভাবে সাজানো যায়” ধরনের উত্তর প্রায়ই শত শত অঙ্কের। পুরো সংখ্যা ছাপাতে big integer লাগত, আর সময় বাড়ত। তাই প্রশ্নে বলা থাকে “উত্তর 10⁹ + 7 দিয়ে ভাগশেষ দাও”। এই সংখ্যাটি বাছার কারণ: (১) এটি মৌলিক, তাই 1…p − 1-এর প্রতিটির inverse আছে — ভাগ করা যায়; (২) এটি 2³⁰-এর চেয়ে ছোট, তাই দুটি ভাগশেষের যোগফল 32-bit signed int-এ আঁটে; (৩) দুটির গুণফল < 2⁶³, তাই long long-এ আঁটে। আরেকটি প্রচলিত মৌলিক 998244353 = 119·2²³ + 1, যা number-theoretic transform-এর (দ্রুত বহুপদী গুণ) উপযোগী।
দ্রুত modular ঘাত
aᵉ mod m-এ e-বার গুণ নয়: পর্ব ৪৯-এর (Divide and conquer) binary exponentiation — e-কে binary-তে ভেঙে বারবার বর্গ, প্রতিটি ধাপে mod. গুণ লাগে O(log e), আর m ≤ 2⁶³ হলে প্রতিটি গুণ 128-bit-এ (__int128) করে mod নিতে হয়। Python-এ তৈরি আছে: pow(a, e, m). ছোট উদাহরণ, 3¹³ mod 7: 13 = 1101₂; বারবার বর্গে 3¹ = 3, 3² ≡ 2, 3⁴ ≡ 4, 3⁸ ≡ 2; যে bit-গুলো 1 (8, 4, 1), তাদের গুণ: 2·4·3 = 24 ≡ 3. ১২টি গুণের বদলে ৫টি।
Modular inverse — দুই পথ
a-এর modular inverse x, যাতে a·x ≡ 1 (mod m). যেমন mod 7-এ 3⁻¹ = 5, কারণ 15 = 2·7 + 1. inverse থাকে ঠিক তখন, যখন gcd(a, m) = 1 (gcd = g > 1 হলে a·x − k·m সবসময় g-এর গুণিতক, কখনো 1 নয়)।
১২ ঘণ্টার ঘড়ির কাঁটা 12-এ আছে। প্রতিবার 5 ঘণ্টা করে ঘোরান: 5, 10, 3, 8, 1 — পঞ্চম বারে কাঁটা 1-এ, অর্থাৎ 5·5 ≡ 1 (mod 12), 5-এর inverse 5. এবার 3 ঘণ্টা করে ঘোরান: 3, 6, 9, 12, 3, … — কাঁটা কখনো 1-এ পৌঁছায় না, কারণ gcd(3, 12) = 3 আর কাঁটা শুধু 3-এর গুণিতকে থামে। inverse থাকা মানে “ধাপে ধাপে ঘুরে 1-এ পৌঁছানো যায়”।
- পথ ১ — extended Euclid: a·x + m·y = 1 থেকে mod m নিলে a·x ≡ 1. যেকোনো m-এ চলে (শুধু gcd = 1 লাগে), O(log m).
- পথ ২ — Fermat: Fermat's little theorem বলে, p মৌলিক আর p ∤ a হলে aᵖ⁻¹ ≡ 1 (mod p). তাই a · aᵖ⁻² ≡ 1, অর্থাৎ a⁻¹ ≡ aᵖ⁻² (mod p) — একটি দ্রুত ঘাত, O(log p). শুধু মৌলিক mod-এ।
Fermat-এর উপপাদ্যের ছোট প্রমাণ: 1, 2, …, p − 1-কে a দিয়ে গুণ করে mod p নিন। কোনো দুটি সমান হতে পারে না (a·i ≡ a·j হলে p | a(i − j), p ∤ a বলে p | (i − j), তাই i = j), আর কেউ 0 নয়। তাই a, 2a, …, (p−1)a আসলে 1, …, p − 1-এরই অন্য ক্রম। দুই তালিকার গুণফল সমান: aᵖ⁻¹·(p−1)! ≡ (p−1)!, আর (p−1)!-এর inverse আছে বলে কেটে দিলে aᵖ⁻¹ ≡ 1. একটি উদাহরণে কাজে লাগাই: 3²⁰⁰ mod 13? 3¹² ≡ 1, আর 200 = 16·12 + 8, তাই 3²⁰⁰ ≡ 3⁸ = 6561 ≡ 9.
৭ · nCr mod p, Euler-এর φ আর Chinese remainder theorem
Factorial টেবিল দিয়ে nCr mod p
“n জনের ক্রিকেট স্কোয়াড থেকে r জনের দল কতভাবে?” — C(n, r) = n! / (r!·(n−r)!). mod p-এ ভাগ মানে inverse দিয়ে গুণ। অনেক প্রশ্নের জন্য একবার টেবিল বানান: fact[i] = fact[i−1]·i; তারপর শুধু inv_fact[N] = fact[N]ᵖ⁻² একটি দ্রুত ঘাতে, আর নিচে নামতে inv_fact[i−1] = inv_fact[i]·i (কারণ 1/(i−1)! = i/i!). এরপর প্রতিটি প্রশ্ন:
প্রস্তুতি O(N + log p), প্রতিটি প্রশ্ন O(1), জায়গা O(N). শর্ত: n < p, নইলে n!-এ p নিজেই ঢুকে 0 করে দেয়; n ≥ p হলে লাগে Lucas's theorem. কনটেস্টে n ≤ 10⁶ আর p = 10⁹ + 7 — শর্ত নিশ্চিন্তে মানে।
Euler-এর totient φ(n) আর Euler-এর উপপাদ্য
φ(n) = 1…n-এর মধ্যে n-এর সাথে সহমৌলিক সংখ্যা কয়টি। φ(12) = 4 (1, 5, 7, 11); মৌলিক p-এ φ(p) = p − 1. সূত্র: n-এর প্রতিটি আলাদা মৌলিক উৎপাদক p-এর জন্য “p-এর গুণিতক বাদ” — φ(n) = n·∏(1 − 1/p). 36 = 2²·3², তাই φ(36) = 36 · ½ · ⅔ = 12. একটি n-এর জন্য trial division-এ O(√n); 1…N সবার জন্য sieve-এর মতো করে O(N log log N).
Euler-এর উপপাদ্য: gcd(a, n) = 1 হলে a^φ(n) ≡ 1 (mod n) — Fermat-এর সাধারণ রূপ, প্রমাণও একই (সহমৌলিক φ(n)টি সংখ্যাকে a দিয়ে গুণ করলে আবার তারাই)। উদাহরণ: 5⁴ = 625 = 52·12 + 1 ≡ 1 (mod 12). ফলে যেকোনো mod n-এ inverse = a^(φ(n)−1), আর খুব বড় ঘাতকে ছোট করা যায়: aᵉ ≡ a^(e mod φ(n)).
Chinese remainder theorem — Sunzi-র ধাঁধা
প্রাচীন চীনা গণিতগ্রন্থ Sunzi Suanjing-এর (খ্রিস্টীয় তৃতীয় থেকে পঞ্চম শতকের মধ্যে কোনো সময়) একটি প্রশ্ন: কিছু জিনিস আছে, সংখ্যা অজানা। ৩টি করে গুনলে ২টি বাকি থাকে, ৫টি করে গুনলে ৩টি, ৭টি করে গুনলে ২টি। জিনিস কয়টি?
Chinese remainder theorem (CRT) বলে, mod-গুলো জোড়ায় জোড়ায় সহমৌলিক হলে M = 3·5·7 = 105-এর ভেতরে উত্তর ঠিক একটি। গঠন: প্রতিটি mᵢ-এর জন্য Mᵢ = M / mᵢ, আর yᵢ = Mᵢ⁻¹ mod mᵢ; তাহলে x = ∑ rᵢ·Mᵢ·yᵢ mod M. এখানে M₁ = 35 ≡ 2 (mod 3), y₁ = 2; M₂ = 21 ≡ 1 (mod 5), y₂ = 1; M₃ = 15 ≡ 1 (mod 7), y₃ = 1. x = 2·35·2 + 3·21·1 + 2·15·1 = 140 + 63 + 30 = 233 ≡ 23 (mod 105). যাচাই: 23 = 7·3 + 2 = 4·5 + 3 = 3·7 + 2. কেন কাজ করে: rᵢ·Mᵢ·yᵢ পদটি mod mᵢ-এ rᵢ, আর বাকি সব mᵢ-এর গুণিতক বলে 0. প্রতিটি inverse extended Euclid-এ আসে, তাই মোট সময় O(k log M). mod-গুলো সহমৌলিক না হলে সাবধান: x ≡ r₁ (mod m₁) আর x ≡ r₂ (mod m₂)-এর সমাধান থাকে ঠিক তখন, যখন gcd(m₁, m₂) দিয়ে r₂ − r₁ বিভাজ্য; তখন extended Euclid দিয়ে দুটি মিলে একটি শর্ত হয়, mod lcm(m₁, m₂)-এ। যেমন x ≡ 1 (mod 4) আর x ≡ 2 (mod 6)-এর কোনো সমাধান নেই — প্রথমটি বলে x বিজোড়, দ্বিতীয়টি বলে জোড়। বাস্তবে CRT দিয়ে বড় mod-এর হিসাব কয়েকটি ছোট মৌলিক mod-এ ভাগ করে সমান্তরালে চালানো যায় (যেমন RSA-র ডিক্রিপশন দ্রুত করতে)।
৮ · Miller–Rabin — বড় সংখ্যা মৌলিক কি না, দ্রুত
n ≈ 10¹⁸ হলে trial division-এ 10⁹ ভাগ — কনটেস্টে অসম্ভব। Fermat-এর উপপাদ্য উল্টে একটি পরীক্ষা বানানো যায়: কোনো a-তে aⁿ⁻¹ ≢ 1 (mod n) হলে n নিশ্চিতভাবে যৌগিক। কিন্তু উল্টোটা সত্য নয়। 561 = 3·11·17 যৌগিক, তবু 561-এর সাথে সহমৌলিক প্রতিটি a-তে a⁵⁶⁰ ≡ 1 (mod 561). এমন সংখ্যাকে বলে Carmichael number, আর এরা অসীম সংখ্যক — Fermat-এর পরীক্ষা এদের কাছে অন্ধ।
Miller–Rabin আরেকটি সত্য যোগ করে: মৌলিক p-এ x² ≡ 1 মানে p | (x − 1)(x + 1), তাই x ≡ ±1 — 1-এর বর্গমূল শুধু 1 আর −1. n − 1 = d·2ˢ লিখুন (d বিজোড়)। x = a^d থেকে s − 1 বার বর্গ করে দেখুন: n মৌলিক হলে ক্রমটি হয় 1 দিয়ে শুরু হয়, নয়তো কোথাও n − 1 আসে। n − 1 না এসে 1-এ পৌঁছানো মানে 1-এর একটি “অস্বাভাবিক” বর্গমূল পাওয়া গেল — n নিশ্চিতভাবে যৌগিক, আর a হলো তার witness (সাক্ষী)।
| ধাপ | |
| x mod 561 | |
| x = 560 (≡ −1)? | |
| সিদ্ধান্ত |
- ভাঙা। 560-কে যতবার পারা যায় 2 দিয়ে ভাগ: 560 = 35·2⁴. তাই d = 35, s = 4; শিকলে a^d থেকে আরও তিনটি বর্গ দেখতে হবে।
- শুরু। দ্রুত ঘাতে 2³⁵ mod 561 = 263. এটি 1 বা 560 হলে 2 এখানেই “পাস” দিত; হয়নি, তাই বর্গ করতে থাকি।
- প্রথম বর্গ। 263² mod 561 = 166 — এখনো 560 আসেনি।
- দ্বিতীয় বর্গ। 166² mod 561 = 67. শেষ একটি সুযোগ বাকি।
- তৃতীয় বর্গ — ধরা। 67² ≡ 1, অথচ 67 নিজে 1 বা 560 নয় — 1-এর অস্বাভাবিক বর্গমূল, যা মৌলিক mod-এ অসম্ভব। তাই 561 যৌগিক। পরের বর্গ 2⁵⁶⁰ ≡ 1 — শুধু এটি দেখা Fermat-এর পরীক্ষা ঠকে যেত। বোনাস: gcd(66, 561) = 33 আর gcd(68, 561) = 17 — উৎপাদকও বেরিয়ে এল।
কতটা নির্ভরযোগ্য? যৌগিক n-এর জন্য 1…n − 1-এর অন্তত ¾ ভাগ a witness (Rabin, ১৯৮০) — এলোমেলো k টি base-এ ভুলের সম্ভাবনা ≤ 4⁻ᵏ: একটি Monte Carlo অ্যালগরিদম, যার ভুল একদিকে (“মৌলিক” উত্তর ভুল হতে পারে, “যৌগিক” কখনো নয়)। আর 64-bit-এর জন্য এলোমেলোতাও লাগে না: প্রথম ১২টি মৌলিক {2, 3, 5, …, 37} base নিলে প্রতিটি n < 2⁶⁴-এ ফল নির্ভুল — পরীক্ষিত সীমাটি 2⁶⁴-এর চেয়ে অনেক বড়, তাই তখন এটি deterministic. প্রতিটি base-এ একটি দ্রুত ঘাত আর ≤ s বর্গ: সময় O(k log n) modular গুণ। n < 2⁶⁴ হলে গুণফল 2¹²⁸ পর্যন্ত যায়, তাই গুণ unsigned __int128-এ।
#include <initializer_list>
#include <iostream>
#include <vector>
using namespace std;
using ll = long long; using u64 = unsigned long long; using u128 = unsigned __int128;
const ll MOD = 1'000'000'007;
ll extgcd(ll a, ll b, ll& x, ll& y) { // returns g and sets a*x + b*y = g
if (b == 0) { x = 1; y = 0; return a; }
ll g = extgcd(b, a % b, y, x); // b*y + (a%b)*x = g
y -= (a / b) * x;
return g;
}
u64 power(u64 b, u64 e, u64 m) { // b^e mod m; products in 128 bits
u64 r = 1; b %= m;
for (; e; e >>= 1, b = (u128)b * b % m) if (e & 1) r = (u128)r * b % m;
return r;
}
bool isPrime(u64 n) { // deterministic Miller-Rabin, 64-bit n
if (n < 2) return false;
u64 d = n - 1; int s = 0;
while (d % 2 == 0) { d /= 2; ++s; } // n - 1 = d * 2^s, d odd
for (u64 a : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}) {
if (n % a == 0) return n == a;
u64 x = power(a, d, n); bool witness = x != 1 && x != n - 1;
for (int r = 1; r < s && witness; ++r) witness = (x = (u128)x * x % n) != n - 1;
if (witness) return false; // a proves n composite
}
return true;
}
int main() {
ll x, y, g = extgcd(3120, 17, x, y);
cout << "gcd=" << g << " x=" << x << " y=" << y << ", 17^-1 mod 3120 = " << (y % 3120 + 3120) % 3120 << '\n';
extgcd(123456789, MOD, x, y);
cout << "inverse of 123456789: extgcd " << (x % MOD + MOD) % MOD << ", fermat " << power(123456789, MOD - 2, MOD) << '\n';
const int N = 1000000; vector<ll> f(N + 1, 1), inv(N + 1);
for (int i = 1; i <= N; ++i) f[i] = f[i - 1] * i % MOD;
inv[N] = power(f[N], MOD - 2, MOD); // one Fermat inverse, then walk down
for (int i = N; i > 0; --i) inv[i - 1] = inv[i] * i % MOD;
auto C = [&](int n, int r) { return r < 0 || r > n ? 0 : f[n] * inv[r] % MOD * inv[n - r] % MOD; };
cout << "C(10,3)=" << C(10, 3) << ", C(1000000,500000) mod p = " << C(N, N / 2) << '\n';
for (u64 n : {561ULL, 3215031751ULL, 1000000007ULL, 999999999989ULL, 18446744073709551557ULL})
cout << n << (isPrime(n) ? " prime" : " composite") << '\n';
}আউটপুট:
gcd=1 x=2 y=-367, 17^-1 mod 3120 = 2753
inverse of 123456789: extgcd 18633540, fermat 18633540
C(10,3)=120, C(1000000,500000) mod p = 996692777
561 composite
3215031751 composite
1000000007 prime
999999999989 prime
18446744073709551557 prime
প্রথম লাইন stepper-এর হুবহু ফল: x = 2, y = −367, inverse 2753 — recursive রূপও iterative টেবিলের একই জোড়া দেয়। দ্বিতীয় লাইনে দুই পথের inverse মিলে গেছে: গুণফল 123456789 × 18633540 ≡ 1 (mod 10⁹ + 7). C(10, 3) = 120 সাধারণ হিসাবেও সত্য — ছোট ইনপুটে মেলানো এ ধরনের কোড যাচাইয়ের সহজ উপায়। শেষ পাঁচটি: 561 (Carmichael) আর 3215031751 = 151·751·28351 যৌগিক — দ্বিতীয়টি base 2, 3, 5, 7 চারটিকেই ঠকায় (প্রতিটিতে strong pseudoprime), তাই শুধু কয়েকটি ছোট base যথেষ্ট নয়। 999999999989 হলো 10¹²-এর নিচের সবচেয়ে বড় মৌলিক, আর 18446744073709551557 = 2⁶⁴ − 59, 64-bit-এর সবচেয়ে বড় মৌলিক। (y % 3120 + 3120) % 3120 — C++-এর ঋণাত্মক ভাগশেষ ঠিক করার প্রচলিত কৌশল।
MOD = 10**9 + 7
def extgcd(a, b): # returns (g, x, y) with a*x + b*y = g
if b == 0:
return a, 1, 0
g, x1, y1 = extgcd(b, a % b)
return g, y1, x1 - (a // b) * y1
def is_prime(n): # deterministic Miller-Rabin, 64-bit n
if n < 2:
return False
d, s = n - 1, 0
while d % 2 == 0: # n - 1 = d * 2^s, d odd
d, s = d // 2, s + 1
for a in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37):
if n % a == 0:
return n == a
x = pow(a, d, n)
if x in (1, n - 1): # this base says "probably prime"
continue
for _ in range(s - 1):
x = x * x % n
if x == n - 1:
break
else: # never reached n - 1: a is a witness
return False
return True
g, x, y = extgcd(3120, 17)
print(f"gcd={g} x={x} y={y}, 17^-1 mod 3120 = {y % 3120}")
_, x, _ = extgcd(123456789, MOD)
print(f"inverse of 123456789: extgcd {x % MOD}, fermat {pow(123456789, MOD - 2, MOD)}")
N = 10**6
f, inv = [1] * (N + 1), [1] * (N + 1)
for i in range(1, N + 1):
f[i] = f[i - 1] * i % MOD
inv[N] = pow(f[N], MOD - 2, MOD) # one Fermat inverse, then walk down
for i in range(N, 0, -1):
inv[i - 1] = inv[i] * i % MOD
C = lambda n, r: 0 if r < 0 or r > n else f[n] * inv[r] % MOD * inv[n - r] % MOD
print(f"C(10,3)={C(10, 3)}, C(1000000,500000) mod p = {C(N, N // 2)}")
for n in (561, 3215031751, 1000000007, 999999999989, 18446744073709551557):
print(n, "prime" if is_prime(n) else "composite")আউটপুট C++-এর সাথে হুবহু এক:
gcd=1 x=2 y=-367, 17^-1 mod 3120 = 2753
inverse of 123456789: extgcd 18633540, fermat 18633540
C(10,3)=120, C(1000000,500000) mod p = 996692777
561 composite
3215031751 composite
1000000007 prime
999999999989 prime
18446744073709551557 prime
for … else খেয়াল করুন: else অংশ চলে শুধু তখন, যখন লুপ break ছাড়া শেষ হয় — অর্থাৎ বর্গের শিকলে n − 1 কখনো আসেনি, a একটি witness. Python-এ % সবসময় অঋণাত্মক, আর int-এর সীমা নেই — x * x % n-এ __int128-এর দরকার পড়েনি। Python 3.8 থেকে pow(a, -1, m) সরাসরি inverse দেয় (ভেতরে extended Euclid). C(10⁶, 5·10⁵)-এর আসল মান প্রায় তিন লাখ অঙ্কের; math.comb(10**6, 5*10**5) % (10**9 + 7) দিয়ে একই 996692777 পাওয়া যায় — কিন্তু অনেক ধীরে, আর প্রতিটি নতুন প্রশ্নে আবার।
আরও গভীরে: 64-bit সংখ্যা উৎপাদকে ভাঙতে কনটেস্টে চলে Miller–Rabin + Pollard's rho (১৯৭৫): x → x² + c (mod n) ক্রমটি mod p-এ (p অজানা উৎপাদক) birthday paradox-এর কারণে প্রায় √p ধাপে চক্রে ঢোকে, Floyd-এর ধীর-দ্রুত pointer (পর্ব ১২) দিয়ে সেই চক্র ধরা হয়, আর gcd(|x − y|, n) উৎপাদকটি বের করে — প্রত্যাশিত ধাপ প্রায় n1/4, 10¹⁸-এ মোটামুটি 3 × 10⁴. কিন্তু শত শত অঙ্কের n = p·q-তে সবচেয়ে ভালো জানা পদ্ধতিও (general number field sieve) অসম্ভব রকম ধীর, আর এর ওপরই দাঁড়িয়ে RSA (১৯৭৭): n আর e প্রকাশ্য, গোপন চাবি d = e⁻¹ mod φ(n) — ঠিক অংশ ৫-এর হিসাব। খেলনা উদাহরণ p = 61, q = 53: n = 3233, φ = 3120, e = 17, d = 2753; বার্তা 65 → 65¹⁷ mod 3233 = 2790 → 2790²⁷⁵³ mod 3233 = 65. Euler-এর উপপাদ্যই নিশ্চিত করে ঘুরে মূল বার্তা ফেরে। factoring যে কঠিন, তা প্রমাণিত নয় — বহু দশকের চেষ্টার ভিত্তিতে বিশ্বাস।
নিজের হাতে ক্রিপ্টোগ্রাফি বানাবেন না। উপরের “textbook RSA” শেখার জন্য, ব্যবহারের জন্য নয় — padding ছাড়া একই বার্তা সবসময় একই ciphertext দেয়, ছোট e আর ছোট বার্তায় ভাঙা যায়, আর সময় মেপে (timing attack) চাবি ফাঁস হতে পারে। বাস্তবে OpenSSL, libsodium বা ভাষার মানসম্মত লাইব্রেরি ব্যবহার করুন, আর চাবির মাপ বর্তমান নির্দেশিকা দেখে বাছুন (RSA-তে সাধারণত অন্তত 2048 bit সুপারিশ করা হয়)।
পরীক্ষা ও ইন্টারভিউতে:
- Sieve of Eratosthenes-এর সময় কত, আর কাটা p² থেকে কেন? — O(n log log n), কারণ ∑ n/p ≈ n ln ln n; p²-এর ছোট গুণিতকদের ছোট মৌলিক উৎপাদক আছে, তারা আগেই কাটা।
- gcd(a, b) = gcd(b, a mod b)-এর প্রমাণ দিন। — (a, b) আর (b, a − qb)-এর সাধারণ উৎপাদকের সেট এক।
- Euclid-এর worst case কোন ইনপুট? — পরপর দুটি Fibonacci সংখ্যা; ধাপ O(log min(a, b)).
- mod 10⁹ + 7-এ a / b কীভাবে? — a · b^(p−2) mod p (Fermat), বা extended Euclid-এর inverse; সরাসরি ভাগ ভুল।
- 561 মৌলিক কি না, Fermat পরীক্ষা কেন বিশ্বাসযোগ্য নয়? — 561 = 3·11·17 Carmichael; Miller–Rabin-এ base 2-ই witness.
৯ · দৈনন্দিন জীবনে সংখ্যাতত্ত্ব আর ইতিহাস
OTP আর check digit
অনেক OTP ব্যবস্থার মানদণ্ড (HOTP, TOTP) একটি গোপন চাবি আর সময় থেকে hash বানিয়ে শেষে mod 10⁶ নিয়ে ছয় অঙ্কের কোড দেয়। ব্যাংক কার্ডের শেষ অঙ্ক (Luhn) বা ISBN-এর শেষ অঙ্ক একটি mod-হিসাব — টাইপের ভুল সাথে সাথে ধরা পড়ে।
mod 10, mod 11অনলাইন লেনদেনের নিরাপত্তা
মোবাইল ব্যাংকিং অ্যাপ বা ওয়েবসাইটের HTTPS সংযোগে চাবি বিনিময় আর ডিজিটাল স্বাক্ষর চলে public-key cryptography-তে — RSA বা elliptic curve, দুটিরই ভিত্তি modular arithmetic আর বড় মৌলিক সংখ্যা।
HTTPS, TLSHash আর এলোমেলো সংখ্যা
hash table-এর মৌলিক আকার (পর্ব ৩২), rolling hash-এর mod (পর্ব ৫৮), আর অনেক সরল pseudo-random generator — সবখানে ভাগশেষের হিসাব, যাতে মান সমানভাবে ছড়ায়।
mod pইতিহাস। Euclid-এর Elements (আনুমানিক খ্রিস্টপূর্ব ৩০০)-এর সপ্তম খণ্ডে বারবার বিয়োগ করে সাধারণ মাপ খোঁজার পদ্ধতি আছে, আর নবম খণ্ডে মৌলিক অসীম হওয়ার প্রমাণ। আলেকজান্দ্রিয়ার গ্রন্থাগারের প্রধান Eratosthenes (আনুমানিক খ্রিস্টপূর্ব ২৪০-এর দিকে সক্রিয়) — যিনি পৃথিবীর পরিধিও মেপেছিলেন — তাঁর নামের sieve আমরা জানি পরের লেখক Nicomachus-এর বই থেকে। Pierre de Fermat ১৬৪০ সালের একটি চিঠিতে তাঁর “ছোট উপপাদ্য” প্রমাণ ছাড়া লেখেন; Leonhard Euler ১৭৩৬ সালে প্রমাণ প্রকাশ করেন আর পরে φ দিয়ে সাধারণ রূপ দেন। Carl Friedrich Gauss ১৮০১ সালে, ২৪ বছর বয়সে, Disquisitiones Arithmeticae-তে ≡ চিহ্ন চালু করে congruence-কে একটি পূর্ণ তত্ত্বে দাঁড় করান। ১৯৭৬–৮০ সালে Gary Miller আর Michael Rabin-এর কাজ থেকে Miller–Rabin, ১৯৭৫ সালে Pollard-এর rho, আর ১৯৭৭ সালে Ron Rivest, Adi Shamir ও Leonard Adleman-এর RSA.
১০ · AI-এর যুগে number theory — overflow, ঋণাত্মক mod আর অর্ধেক-সত্য
sieve, gcd, modpow — এসব AI মুহূর্তে লিখে দেয়, আর ছোট উদাহরণে প্রায় সবসময় ঠিক চলে। ভুল লুকায় বড় সংখ্যায় আর কিনারায়: int-এ গুণ, C++-এর ঋণাত্মক %, p যৌগিক হলেও Fermat-এর inverse, আর “Fermat test দিয়ে মৌলিক যাচাই”-এর মতো অর্ধেক-সত্য।
Number theory-তে AI সাধারণত যেসব ভুল করে
| AI-এর ভুল | কীভাবে ধরবেন |
|---|---|
a * b % MOD, যেখানে a, b int — overflow | a = b = 10⁹ + 6 দিয়ে চালান; উত্তর 1 হওয়া উচিত |
বিয়োগের পর % MOD — ঋণাত্মক উত্তর | (3 − 5) mod 7 চেয়ে দেখুন; 5 আসা উচিত |
যৌগিক m-এ pow(a, m - 2, m)-কে inverse বলা | m = 12, a = 5 দিন; ফলকে a দিয়ে গুণ করে 1 আসে কি না দেখুন |
| শুধু Fermat test বা অল্প base-এর Miller–Rabin | 561 আর 3215031751 দিন; দুটিই যৌগিক |
| sieve-কে “O(n log n)” বা trial division-কে “polynomial” বলা | প্রমাণ চান: ∑ 1/p ≈ ln ln n; ইনপুটের bit-এ √n = 2^(b/2) |
a * b / gcd — lcm-এ overflow | a = b = 10¹² দিন; উত্তর 10¹² হওয়া উচিত |
AI-কে কোড লিখতে না বলে বরং আপনার কোডের সীমা আর প্রমাণ নিয়ে প্রশ্ন করুন:
AI-এর (বা নিজের) number theory কোড যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১প্রায় ২৩০০ বছর পুরোনো Euclid-এর অ্যালগরিদম আজও কেন ব্যবহার হয় — আর এর বিশ্লেষণ কম্পিউটার বিজ্ঞানে কী শুরু করেছিল?
Donald Knuth তাঁর The Art of Computer Programming-এ Euclid-এর পদ্ধতিকে বলেছেন টিকে থাকা সবচেয়ে পুরোনো অ-তুচ্ছ অ্যালগরিদমগুলোর একটি। Elements-এ এটি লেখা হয়েছিল জ্যামিতির ভাষায় — দুটি রেখাংশের “সাধারণ মাপ” খুঁজতে বড়টি থেকে ছোটটি বারবার বিয়োগ। আজকের রূপে বিয়োগের বদলে ভাগশেষ, কিন্তু মূল যুক্তি হুবহু এক।
এটি টিকে আছে কারণ এর চেয়ে ভালো কিছু প্রায় নেই। ধাপ O(log min(a, b)), আর উৎপাদকে ভাঙার দরকার পড়ে না — যা বড় সংখ্যায় অসম্ভব রকম কঠিন। RSA-র চাবি বানাতে e আর φ(n) সহমৌলিক কি না দেখা, আর d = e⁻¹ বের করা — দুটিই শত শত অঙ্কের সংখ্যায় extended Euclid. ভগ্নাংশকে লঘিষ্ঠ আকারে আনা, CRT-র inverse, কম্পিউটার বীজগণিতে বহুপদীর gcd — সবখানে একই অ্যালগরিদম।
১৮৪৪ সালে Gabriel Lamé দেখান, ধাপের সংখ্যা ছোট সংখ্যার অঙ্কের পাঁচ গুণের বেশি নয়, আর সবচেয়ে খারাপ ক্ষেত্র Fibonacci সংখ্যা। একটি অ্যালগরিদম কত ধাপ নেয় — এই প্রশ্নকে গাণিতিকভাবে উত্তর দেওয়ার প্রথম দিকের উদাহরণ হিসেবে একে প্রায়ই উল্লেখ করা হয়; অর্থাৎ আজকের complexity বিশ্লেষণের একটি পূর্বসূরি। পরে আরও সূক্ষ্ম প্রশ্ন এসেছে: গড়ে কত ধাপ? এর উত্তরে দেখা গেছে গড় ধাপও লগারিদমিক, একটি নির্দিষ্ট ধ্রুবকসহ।
কম্পিউটারের যুগে একটি বৈকল্পিকও এসেছে — Josef Stein-এর ১৯৬৭ সালের binary GCD, যা ভাগের বদলে শুধু shift আর বিয়োগ ব্যবহার করে, কারণ অনেক যন্ত্রে ভাগ ধীর। বড় সংখ্যার লাইব্রেরিগুলো আরও উন্নত রূপ ব্যবহার করে। তবু প্রতিটির ভেতরে Euclid-এর সেই পর্যবেক্ষণ: সাধারণ উৎপাদক বদলায় না।
মূল উপলব্ধি: একটি সঠিক invariant (সাধারণ উৎপাদকের সেট অপরিবর্তিত) আর দ্রুত ছোট হওয়া (দুই ধাপে অর্ধেক) — এই দুটি মিললে অ্যালগরিদম সহস্রাব্দ পার করে টিকে যায়।
প্র ০২G. H. Hardy সংখ্যাতত্ত্বকে “ব্যবহারিক কাজে অপ্রয়োজনীয়” বলে গর্ব করতেন। আজ কেন ঠিক উল্টোটা সত্য?
ইংরেজ গণিতবিদ G. H. Hardy ১৯৪০ সালে A Mathematician's Apology বইয়ে লেখেন, খাঁটি গণিত — বিশেষ করে সংখ্যাতত্ত্ব — যুদ্ধ বা বাস্তব কাজে লাগে না, আর এটাই তার সৌন্দর্য। তাঁর সময়ে কথাটি অনেকটা সত্যই ছিল: মৌলিক সংখ্যার বিন্যাস বা congruence-এর তত্ত্ব ছিল নিছক বুদ্ধির আনন্দ।
এর ভিত তৈরি হয়েছিল শতাব্দী ধরে। Fermat-এর ছোট উপপাদ্য (১৬৪০), Euler-এর φ আর তাঁর সাধারণীকরণ, তারপর ১৮০১ সালে Gauss-এর Disquisitiones Arithmeticae — যেখানে ≡ চিহ্ন, ভাগশেষের বীজগণিত আর quadratic reciprocity একটি পূর্ণ তত্ত্বে দাঁড়ায়। Gauss নিজে গণিতকে বিজ্ঞানের রানি আর সংখ্যাতত্ত্বকে গণিতের রানি বলেছিলেন বলে প্রচলিত।
মোড় ঘোরে ১৯৭৬–৭৭ সালে। Whitfield Diffie আর Martin Hellman দেখান, দুজন মানুষ খোলা চ্যানেলে কথা বলেও একটি গোপন চাবিতে একমত হতে পারেন — modular ঘাতের ওপর ভিত্তি করে। পরের বছর Rivest, Shamir আর Adleman আনেন RSA, যার নিরাপত্তা Euler-এর উপপাদ্য আর উৎপাদকে ভাঙার কাঠিন্যের ওপর। পরে জানা যায়, ব্রিটিশ গোয়েন্দা সংস্থা GCHQ-তে Clifford Cocks ১৯৭৩ সালেই প্রায় একই ধারণা পেয়েছিলেন, কিন্তু তা গোপন রাখা হয়েছিল বহু বছর।
আজ ওয়েবসাইটের HTTPS, অনলাইন ব্যাংকিং, সফটওয়্যার আপডেটের ডিজিটাল স্বাক্ষর — প্রতিদিন শতকোটি বার এই “অপ্রয়োজনীয়” গণিত চলে। আর elliptic curve cryptography আরও গভীর সংখ্যাতত্ত্ব টেনে এনেছে। কোয়ান্টাম কম্পিউটারের সম্ভাব্য হুমকির কারণে (Peter Shor-এর ১৯৯৪ সালের অ্যালগরিদম বড় কোয়ান্টাম যন্ত্রে দ্রুত factoring করতে পারবে) গবেষকেরা এখন lattice-ভিত্তিক নতুন ব্যবস্থার দিকে যাচ্ছেন — সেটিও গণিতের আরেকটি “খাঁটি” শাখা।
মূল উপলব্ধি: আজ যে গণিতকে অকাজের মনে হয়, কাল সেটিই অবকাঠামো হতে পারে। সংখ্যাতত্ত্ব শেখা মানে ইন্টারনেটের তালা-চাবির ভেতরের যুক্তি বোঝা।
প্র ০৩কনটেস্টে 10⁹ + 7 আর 998244353 — এই দুটি সংখ্যা কেন এত জনপ্রিয়, আর ভুল mod বাছলে কী সমস্যা হয়?
একটি mod বাছতে প্রোগ্রামিং কনটেস্টের প্রশ্ন-লেখকেরা কয়েকটি শর্ত দেখেন। প্রথমত, মৌলিক হওয়া: তখন 0 ছাড়া প্রতিটি সংখ্যার inverse আছে, তাই nCr, সম্ভাবনা বা ভগ্নাংশের উত্তর (“P · Q⁻¹ mod p”) mod-এর ভেতরেই হিসাব করা যায়। যৌগিক mod-এ, যেমন 10⁹, 2 বা 5-এর গুণিতকের inverse থাকে না — অনেক সূত্র ভেঙে পড়ে।
দ্বিতীয়ত, আকার। 10⁹ + 7 < 2³⁰, তাই দুটি ভাগশেষের যোগফল < 2³¹ — 32-bit signed int-এ আঁটে; গুণফল < 2⁶⁰ — 64-bit-এ নিশ্চিন্তে আঁটে, এমনকি কয়েকটি গুণফল যোগ করে তারপর mod নিলেও। mod যদি 10¹⁸-এর কাছাকাছি হতো, প্রতিটি গুণে 128-bit লাগত, আর ভুলের সুযোগ বাড়ত। তৃতীয়ত, মনে রাখা সহজ: “এক বিলিয়ন সাত”।
998244353-এর গল্প আলাদা: এটি 119 · 2²³ + 1. এমন মৌলিকে 2²³ পর্যন্ত মাপের “ঘাত-২” চক্র (root of unity) আছে, তাই number-theoretic transform — FFT-র পূর্ণসংখ্যা-রূপ — দিয়ে বহুপদী গুণ নির্ভুলভাবে O(n log n)-এ হয়। বহুপদী বা convolution-এর প্রশ্নে তাই এটিই মানদণ্ড হয়ে গেছে।
ভুল বা স্থির mod-এর বিপদ hashing-এ বেশি। 2⁶⁴-এর মতো mod (unsigned overflow-এর ওপর ছেড়ে দেওয়া) যৌগিক, আর এর জন্য নির্দিষ্ট ইনপুট বানিয়ে collision ঘটানো যায় — পর্ব ১১ আর ৫৮-এ দেখেছেন। আবার mod-কে ছোট int-এ রাখতে গিয়ে গুণের আগে long long-এ রূপান্তর ভুলে যাওয়া কনটেস্টের সবচেয়ে সাধারণ “Wrong Answer”-গুলোর একটি — উদাহরণে চলে, বড় test-এ ভাঙে।
মূল উপলব্ধি: mod বাছাই একটি প্রকৌশল-সিদ্ধান্ত: মৌলিকতা দেয় ভাগের অধিকার, আকার দেয় overflow থেকে নিরাপত্তা, আর বিশেষ গঠন দেয় দ্রুত অ্যালগরিদম।
প্র ০৪২০০২ সালে প্রমাণ হলো মৌলিকতা যাচাই polynomial সময়ে deterministic-ভাবে করা যায় — AKS অ্যালগরিদম। তবু বাস্তবে সবাই কেন Miller–Rabin ব্যবহার করে?
“একটি সংখ্যা মৌলিক কি না” — এই প্রশ্নটি বহুকাল জটিলতা-তত্ত্বের একটি খোলা ধাঁধা ছিল। ইনপুটের আকার n-এর bit-সংখ্যা b, তাই trial division-এর √n = 2^(b/2) exponential. ১৯৭৬ সালে Gary Miller একটি deterministic polynomial পরীক্ষা দেন, কিন্তু তার শুদ্ধতা নির্ভর করে generalized Riemann hypothesis-এর ওপর, যা আজও অপ্রমাণিত। ১৯৮০ সালে Michael Rabin সেটিকে এলোমেলো base-এর পরীক্ষায় বদলান — নিঃশর্ত, কিন্তু probabilistic.
২০০২ সালে IIT Kanpur-এর Manindra Agrawal আর তাঁর দুই স্নাতক ছাত্র Neeraj Kayal ও Nitin Saxena প্রমাণ করেন, মৌলিকতা যাচাই নিঃশর্তভাবে, এলোমেলোতা ছাড়াই polynomial সময়ে করা যায় — “PRIMES is in P”. তত্ত্বের জগতে এটি ছিল বিরাট ঘটনা।
তবু বাস্তবে AKS প্রায় কেউ চালায় না। এর polynomial-এর মাত্রা বড় (উন্নত রূপেও মোটামুটি b⁶-এর কাছাকাছি), ধ্রুবকও বড়। Miller–Rabin প্রতি base-এ মোটামুটি b³ (সরল গুণে), আর ৪০টি এলোমেলো base-এ ভুলের সম্ভাবনা সর্বোচ্চ 4⁻⁴⁰ — হার্ডওয়্যারের ত্রুটির সম্ভাবনার চেয়ে অনেক ছোট। 64-bit-এ তো নির্দিষ্ট ১২টি base-এ এটি পুরোপুরি deterministic. ক্রিপ্টোগ্রাফি লাইব্রেরিগুলো তাই চাবির মৌলিক বানাতে Miller–Rabin (কখনো সাথে আরেকটি পরীক্ষা) ব্যবহার করে।
বিশেষ আকারের সংখ্যায় আরও দ্রুত পরীক্ষা আছে। 2ᵖ − 1 আকারের Mersenne সংখ্যার জন্য Lucas–Lehmer test; স্বেচ্ছাসেবকদের প্রকল্প GIMPS ১৯৯৬ সাল থেকে এভাবে সবচেয়ে বড় জানা মৌলিকগুলো খুঁজে চলেছে — ২০২৪ সালে পাওয়া 2¹³⁶²⁷⁹⁸⁴¹ − 1-এর দশমিক অঙ্ক চার কোটির বেশি।
মূল উপলব্ধি: “polynomial” মানেই বাস্তবে দ্রুত নয়, আর “probabilistic” মানেই অনির্ভরযোগ্য নয়। ভুলের সম্ভাবনা যখন নগণ্যের চেয়েও নগণ্য, randomized অ্যালগরিদম প্রায়ই সেরা প্রকৌশল-বাছাই।
প্র ০৫RSA-129 ভাঙতে লেগেছিল ১৭ বছর। factoring-এর অগ্রগতি থেকে চাবির মাপ নিয়ে আমরা কী শিখি?
১৯৭৭ সালে Martin Gardner Scientific American-এ RSA-র কথা লেখেন, সাথে একটি চ্যালেঞ্জ: ১২৯ অঙ্কের একটি n দিয়ে লুকানো একটি বার্তা। লেখকদের তখনকার অনুমানে এটি ভাঙতে লাগবে অকল্পনীয় দীর্ঘ সময়। অথচ ১৯৯৪ সালে Derek Atkins, Michael Graff, Arjen Lenstra আর Paul Leyland-এর নেতৃত্বে প্রায় ৬০০ স্বেচ্ছাসেবকের কম্পিউটার ইন্টারনেটে মিলে কয়েক মাসে n-কে উৎপাদকে ভাঙে। বার্তা ছিল “The Magic Words are Squeamish Ossifrage”.
এত দ্রুত কেন? দুই কারণ। কম্পিউটার অনেক দ্রুত আর সস্তা হয়েছিল, আর অনেক যন্ত্রকে একসাথে কাজে লাগানো গিয়েছিল। কিন্তু বড় কারণ অ্যালগরিদম: quadratic sieve আর পরে number field sieve — trial division বা Pollard rho-র চেয়ে অনেক উন্নত, sub-exponential (যদিও polynomial নয়)। পরে RSA-র নামে আরও চ্যালেঞ্জ-সংখ্যা ভাঙা হয়েছে; ২০২০ সালে ২৫০ অঙ্কের (৮২৯ bit) RSA-250 ভাঙতে প্রায় ২৭০০ core-বছরের সমান হিসাব লেগেছিল।
শিক্ষা হলো, চাবির মাপ একবার ঠিক করে চিরকাল চলে না। একসময় ৫১২ bit যথেষ্ট মনে হতো, এখন তা ভাঙা যায়; ১০২৪ bit-কে আর নিরাপদ ধরা হয় না, আর বর্তমান নির্দেশিকা সাধারণত RSA-তে অন্তত ২০৪৮ bit সুপারিশ করে, দীর্ঘ মেয়াদে আরও বড়। একই নিরাপত্তায় elliptic curve অনেক ছোট চাবিতে কাজ করে, তাই আধুনিক HTTPS-এ এর ব্যবহার বেড়েছে।
আর দিগন্তে আছে কোয়ান্টাম কম্পিউটার: Shor-এর অ্যালগরিদম যথেষ্ট বড় ও নির্ভুল কোয়ান্টাম যন্ত্রে RSA আর elliptic curve দুটিকেই ভাঙতে পারবে। এমন যন্ত্র এখনো নেই, তবু আগাম প্রস্তুতি হিসেবে NIST ২০২৪ সালে প্রথম post-quantum মানদণ্ডগুলো প্রকাশ করেছে।
মূল উপলব্ধি: ক্রিপ্টোগ্রাফির নিরাপত্তা একটি চলমান হিসাব — হার্ডওয়্যার আর অ্যালগরিদম দুটিই এগোয়। তাই নিজে বানানো নয়, হালনাগাদ লাইব্রেরি আর বর্তমান নির্দেশিকা মেনে চলাই নিরাপদ পথ।
অনুশীলন
খাতা-কলম নিন। প্রথম তিনটি হাতে করার, শেষটিতে কোড লিখতে হবে।
-
হাতে trace করুন: extended Euclid দিয়ে 240·x + 46·y = gcd(240, 46)-এর x, y বের করুন। প্রতিটি সারির r, x, y লিখুন।
সারিগুলো (r, x, y): (240, 1, 0), (46, 0, 1); q = 5 → (10, 1, −5); q = 4 → (6, −4, 21); q = 1 → (4, 5, −26); q = 1 → (2, −9, 47); q = 2 → r = 0, থামুন।
gcd = 2, আর 240·(−9) + 46·47 = −2160 + 2162 = 2. ৫টি ভাগ লেগেছে। gcd 1 নয়, তাই mod 240-এ 46-এর inverse নেই — কিন্তু 240x + 46y = 10-এর মতো সমীকরণের সমাধান আছে (2 | 10): x = −45, y = 235.
-
Sieve হাতে চালান: 50 পর্যন্ত sieve of Eratosthenes চালালে কোন কোন p-এর জন্য কাটতে হবে? মোট কয়টি মৌলিক থাকবে?
p = 2, 3, 5, 7 — কারণ 7² = 49 ≤ 50, কিন্তু 11² = 121 > 50. 7-এর জন্য কাটা পড়ে শুধু 49 (নতুন)। বাকি মৌলিক ১৫টি: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47.
যাচাই হিসেবে n / ln n ≈ 50 / 3.9 ≈ 12.8 — প্রকৃত সংখ্যার কাছাকাছি, তবে ছোট n-এ সূত্রটি একটু কম বলে।
-
CRT আর inverse: এমন সবচেয়ে ছোট ধনাত্মক x বের করুন যাতে x ≡ 1 (mod 4), x ≡ 2 (mod 3), x ≡ 3 (mod 5). তারপর বলুন mod 12-এ 5-এর inverse কত, আর Fermat-এর সূত্র 5¹⁰ mod 12 কেন এখানে চলবে না।
M = 60. M₁ = 15 ≡ 3 (mod 4), inverse 3; M₂ = 20 ≡ 2 (mod 3), inverse 2; M₃ = 12 ≡ 2 (mod 5), inverse 3. x = 1·15·3 + 2·20·2 + 3·12·3 = 45 + 80 + 108 = 233 ≡ 53 (mod 60). যাচাই: 53 = 13·4 + 1 = 17·3 + 2 = 10·5 + 3.
mod 12-এ 5·5 = 25 ≡ 1, তাই 5⁻¹ = 5. Fermat-এর aᵖ⁻² শুধু মৌলিক p-এ; 12 যৌগিক। Euler-এর রূপ চলে: φ(12) = 4, তাই 5⁻¹ ≡ 5³ = 125 ≡ 5 — মিলে গেল। অথচ 5¹⁰ = 5^(4·2+2) ≡ 5² ≡ 1, যা inverse নয়।
-
কোড লিখুন: 1 থেকে N পর্যন্ত প্রতিটি সংখ্যার φ একসাথে বের করুন, sieve-এর মতো করে। N = 10 হলে φ(1) + … + φ(10) কত?
phi[i] = i দিয়ে শুরু করুন। প্রতিটি i-তে phi[i] = i থাকলে i মৌলিক (কেউ ছোঁয়নি); তখন i-এর প্রতিটি গুণিতক j-তে phi[j] -= phi[j] / i — সূত্রের (1 − 1/p) গুণটি।
vector<int> phiTable(int N) { vector<int> phi(N + 1); for (int i = 0; i <= N; ++i) phi[i] = i; for (int i = 2; i <= N; ++i) if (phi[i] == i) // untouched: i is prime for (int j = i; j <= N; j += i) phi[j] -= phi[j] / i; return phi; }N = 10-এ φ = 1, 1, 2, 2, 4, 2, 6, 4, 6, 4, যোগফল 32. সময় sieve-এর মতোই O(N log log N), জায়গা O(N). phi[j] / i সবসময় নিঃশেষে ভাগ হয়, কারণ তখনো phi[j]-এ i-এর উৎপাদক অক্ষত আছে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমMiller–Rabin-এ একটি base a দিয়ে বিজোড় n পরীক্ষার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- n − 1 = d · 2ˢ লেখা, যেখানে d বিজোড়
- দ্রুত ঘাতে x = a^d mod n বের করা
- x = 1 বা x = n − 1 হলে এই base-এ পাস
- না হলে সর্বোচ্চ s − 1 বার x = x² mod n করে n − 1 খোঁজা
- কখনো n − 1 না এলে n নিশ্চিতভাবে যৌগিক ঘোষণা করা
1 থেকে n পর্যন্ত সব মৌলিক সংখ্যা বের করতে sieve of Eratosthenes-এর সময় কত?
- O(n√n)
- O(n log n)
- O(n log log n)
- ঠিক O(n), প্রতিটি যৌগিক একবার কাটা হয় বলে
Euclid-এর gcd অ্যালগরিদমে সবচেয়ে বেশি ভাগের ধাপ লাগে কোন ধরনের ইনপুটে (একই আকারের সংখ্যার মধ্যে)?
- দুটি বড় মৌলিক সংখ্যা
- পরপর দুটি Fibonacci সংখ্যা
- একটি সংখ্যা অন্যটির গুণিতক
- দুটি সমান সংখ্যা
mod 7-এ 3-এর modular inverse কত?
- 3
- 2
- mod-এ inverse থাকে না, কারণ 7 মৌলিক
- 5
C++-এ a, b দুটিই প্রায় 10¹² মানের long long. lcm(a, b) নিরাপদে বের করার সঠিক উপায় কোনটি (g = gcd(a, b), উত্তর long long-এ আঁটে ধরে নিন)?
- a / g * b
- a * b / g
- g * g
- (a * b) % g
561 = 3 · 11 · 17. 561-এর সাথে সহমৌলিক প্রতিটি a-তে a⁵⁶⁰ ≡ 1 (mod 561). এ থেকে কোনটি সঠিক?
- 561 মৌলিক
- Miller–Rabin-ও 561-কে মৌলিক বলবে
- 561 একটি Carmichael number, যা Fermat-এর পরীক্ষাকে ঠকায়
- φ(561) = 560
কনটেস্টে mod হিসেবে 10⁹ + 7 বাছার পেছনে কোনটি কারণ নয়?
- এটি মৌলিক, তাই 0 ছাড়া প্রতিটি সংখ্যার inverse আছে
- দুটি ভাগশেষের যোগফল 32-bit signed int-এ আঁটে
- দুটি ভাগশেষের গুণফল 64-bit int-এ আঁটে
- এটি থাকলে modular ঘাত O(1)-এ হয়
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- trial division √n-এ থামে (যৌগিকের একটি উৎপাদক ≤ √n); sieve O(n log log n), linear sieve O(n) আর spf দিয়ে উৎপাদকে ভাঙা O(log n).
- gcd(a, b) = gcd(b, a mod b) — সাধারণ উৎপাদকের সেট এক; দুই ধাপে অর্ধেক, তাই O(log min); worst case Fibonacci. lcm = a / g · b.
- Extended Euclid: invariant r = a·x + b·y; gcd = 1 হলে x = a⁻¹ mod b.
- mod-এ যোগ-বিয়োগ-গুণ চলে, ভাগ মানে inverse; মৌলিক p-এ a⁻¹ = aᵖ⁻²; nCr = fact · inv_fact · inv_fact, প্রতিটি O(1).
- φ(n) = n∏(1 − 1/p), a^φ(n) ≡ 1; CRT সহমৌলিক mod-এ একটি উত্তর দেয়; Miller–Rabin ১২টি base-এ 64-bit-এ নির্ভুল, Fermat একা নয় (561).
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৬৩ · Computational geometry — cross product, convex hullপরবর্তী পর্ববিন্দু আর রেখার অ্যালগরিদম — overflow-সচেতন পূর্ণসংখ্যার হিসাবসহ।
- পর্ব ৪৯ · Divide and conquer — Strassen, closest pair আর Karatsubaভিত্তিbinary exponentiation আর বড় সংখ্যার দ্রুত গুণ।
- পর্ব ০৬ · DSA-র গণিত — log, যোগফল আর ধারাসম্পর্কিতharmonic series আর log-এর হিসাব, যা sieve-এর বিশ্লেষণে লেগেছে।
- ডেটা স্ট্রাকচার ও অ্যালগরিদম · কোর্স হোম৭২টি পর্বপুরো কোর্সের রোডম্যাপ — নয়টি মডিউল।
- C++ প্রোগ্রামিং · STL algorithmsভাষার দিক
std::gcd,std::lcmআর তৈরি অ্যালগরিদমের ব্যবহার। - Python প্রোগ্রামিং · সংখ্যা আর গণিতপাশের ট্র্যাকবড় int,
pow(a, e, m),math.gcdআরmath.comb.