সরাসরি পর্বে যান
পর্ব ২২ · ৮৩-এর মধ্যে · মডিউল ৪

Modular arithmetic — ঘড়ির গণিত আর modular exponentiation

Modular Arithmetic — Congruence, Congruence Classes and Z_m, Arithmetic mod m, Divisibility Tests for 3, 9 and 11, Modular Exponentiation by Repeated Squaring, Last Digits and Weekday Problems

Taught in Bangla, this lesson defines congruence mod m, builds the congruence classes of ℤm with addition and multiplication tables, and proves which operations respect congruence and why cancellation can fail. It proves the divisibility tests for 3, 9 and 11, and computes 7^222 mod 1000 = 49 by repeated squaring in 12 multiplications. In-page Python checks the tests on thousands of numbers and solves last-digit, weekday and delivery-cycle problems.

এই পর্বে যা শিখবেন

  • Congruence-এর সংজ্ঞা, তার তিনটি সমতুল্য রূপ, আর কেন এটি একটি equivalence relation, প্রমাণসহ
  • ℤm-এর যোগ আর গুণের ছক, আর কেন যোগ-গুণ congruence মেনে চলে কিন্তু কাটাকাটি নয়
  • 3, 9 আর 11 দিয়ে বিভাজ্যতার পরীক্ষার প্রমাণ, আর 7 ও 13-এর জন্য তিন অঙ্কের দল
  • Repeated squaring দিয়ে বিশাল ঘাতের ভাগশেষ, গুণের সংখ্যার প্রমাণসহ হিসাব
  • শেষ অঙ্ক, সপ্তাহের দিন আর বাজারঘরের ডেলিভারি-চক্রের অঙ্ক, আর একটি পরিচিত ভুল প্রমাণ ধরা

১ · ঘড়ির গণিত — congruence কী

দেয়ালঘড়িতে এখন ৯টা। ৫ ঘণ্টা পরে কয়টা বাজবে? কেউ “১৪টা” বলেন না, বলেন “২টা”। ঘড়ির কাঁটা 12-এ পৌঁছে আবার শুরু থেকে গোনে, তাই ঘড়ির কাছে 14 আর 2 একই জিনিস। সপ্তাহের দিনও তাই: আজ বৃহস্পতিবার হলে ঠিক ৭ দিন, ১৪ দিন বা ৭০ দিন পরেও বৃহস্পতিবার। ট্রেনের সময়সূচি, পরীক্ষার রোল নম্বর দিয়ে বেঞ্চ ভাগ করা, প্রতি তিন দিন পরপর পালা করে বাজার করা, সবখানে আমরা আসলে একটি সংখ্যাকে কোনো নির্দিষ্ট সংখ্যা দিয়ে ভাগ করে শুধু ভাগশেষ মনে রাখি। এই হিসাবের গণিতই modular arithmetic, বাংলায় অনেকে বলেন “ঘড়ির গণিত”।

বাজারঘরে এই হিসাব সব জায়গায়। ঢাকা থেকে সিলেটে ভ্যান যায় প্রতি ১১ দিনে একবার; গ্রাহকের id-কে 16টি সার্ভারের একটিতে পাঠাতে লাগে id mod 16; অর্ডার নম্বরের শেষে একটি check digit বসে, যা টাইপের ভুল ধরে। পর্ব ২০ “বিভাজ্যতা আর মৌলিক সংখ্যা — division algorithm আর unique factorization”-এ ভাগ আর ভাগশেষের ভিত্তি তৈরি হচ্ছে, আর পর্ব ২১ “GCD আর Euclid-এর algorithm — Bézout আর extended Euclid”-এ gcd. এই পর্বে সেই ভাগশেষ দিয়ে হিসাব করার নিয়মগুলো প্রমাণসহ দাঁড় করানো হবে।

সংজ্ঞা ১.১ (congruence)

m একটি ধনাত্মক পূর্ণসংখ্যা, a আর b যেকোনো পূর্ণসংখ্যা। a, b-এর সঙ্গে m মডুলো congruent, লেখা হয়

a ≡ b (mod m)

যদি m ∣ (a − b), অর্থাৎ a − b সংখ্যাটি m দিয়ে নিঃশেষে বিভাজ্য। m-কে বলে modulus. m ∤ (a − b) হলে লিখি a ≢ b (mod m).

চিহ্নগুলো পড়ার নিয়ম: “∣” মানে “ভাগ করে” (m ∣ n মানে n = mk কোনো পূর্ণসংখ্যা k-এর জন্য), “∤” মানে “ভাগ করে না”, আর “≡” পড়া হয় “congruent”. উদাহরণ: 15 ≡ 3 (mod 12), কারণ 15 − 3 = 12; 100 ≡ 2 (mod 7), কারণ 98 = 7 · 14; −3 ≡ 4 (mod 7), কারণ −3 − 4 = −7; আর 17 ≢ 3 (mod 5), কারণ 14-কে 5 ভাগ করে না।

এর পাশেই আছে mod অপারেশন। Division algorithm বলে, যেকোনো পূর্ণসংখ্যা a আর ধনাত্মক m-এর জন্য একমাত্র q আর r আছে, যাতে a = qm + r আর 0 ≤ r < m. এই r-ই a mod m. এর অস্তিত্বের প্রমাণ well-ordering দিয়ে পর্ব ১১-এ হয়েছে, আর একমাত্রতার প্রমাণ পর্ব ২০-এর বিষয়। দুটি জিনিস আলাদা রাখুন: “a ≡ b (mod m)” একটি সম্পর্ক (সত্য বা মিথ্যা একটি বাক্য), আর “a mod m” একটি সংখ্যা (একটি ফাংশনের মান)।

উপপাদ্য ১.২ (তিনটি সমতুল্য রূপ)

m ধনাত্মক হলে নিচের তিনটি বাক্য সমতুল্য: (ক) a ≡ b (mod m); (খ) কোনো পূর্ণসংখ্যা k-এর জন্য a = b + km; (গ) a mod m = b mod m.

প্রমাণ: তিনটি সমতুল্য দেখাতে (ক) ⇔ (খ) আর (ক) ⇔ (গ) দেখানোই যথেষ্ট।

  1. (ক) ⇔ (খ): m ∣ (a − b) মানে কোনো k-এর জন্য a − b = km, অর্থাৎ a = b + km. দুই দিকেই একই সমীকরণ। — ∣-এর সংজ্ঞা
  2. (গ) ⇒ (ক): ধরি a = q1m + r আর b = q2m + r, একই ভাগশেষ r. বিয়োগ করলে a − b = (q1 − q2)m, তাই m ∣ (a − b). — division algorithm
  3. (ক) ⇒ (গ): ধরি b = qm + r, 0 ≤ r < m, আর (খ) অনুযায়ী a = b + km. তাহলে a = (q + k)m + r, যেখানে এখনো 0 ≤ r < m. — বীজগণিত
  4. Division algorithm-এর ভাগশেষ একমাত্র, তাই a-এর ভাগশেষও এই r. অর্থাৎ a mod m = r = b mod m. ∎ — ভাগশেষের একমাত্রতা

রূপ (গ) প্রতিদিনের অর্থ দেয় (“একই ভাগশেষ”), রূপ (খ) প্রমাণে সবচেয়ে কাজের (“m-এর কোনো গুণিতক যোগ করলে আসে”), আর সংজ্ঞার রূপ (ক) ঋণাত্মক সংখ্যাতেও কোনো ঝামেলা ছাড়া কাজ করে।

ঋণাত্মক সংখ্যা আর প্রোগ্রামিং ভাষা: গণিতের সংজ্ঞায় −17 mod 5 = 3, কারণ −17 = (−4) · 5 + 3 আর 0 ≤ 3 < 5. Python-এর % ঠিক এই উত্তর দেয়: -17 % 5 = 3, কারণ Python-এর // নিচের দিকে (floor) গোল করে। কিন্তু C, C++, Java আর JavaScript-এ ভাগফল শূন্যের দিকে কাটা হয়, তাই সেখানে -17 % 5 = −2. দুটোই congruence-এর দিক থেকে ঠিক (−2 ≡ 3 (mod 5)), কিন্তু “ভাগশেষ 0 থেকে m − 1-এর মধ্যে” ধরে লেখা কোড C-তে ভুল করে, যেমন ঋণাত্মক id-এর hash থেকে অ্যারের ঋণাত্মক index. Python-এর দিকটি Python কোর্সের পর্ব ০৭-এ আর C-র দিকটি C প্রোগ্রামিং কোর্সের পর্ব ০৭-এ আছে। C-তে নিরাপদ লেখা: ((a % m) + m) % m.

২ · Congruence class আর ℤm

সপ্তাহের হিসাবে দিন 4, 11, 18, 25, আর পেছনের দিকে −3, −10, সবাই একই বার। ঘড়ির চোখে এরা একটি দল। প্রতিটি পূর্ণসংখ্যা ঠিক একটি দলে পড়ে, আর দল আছে ঠিক ৭টি। গণিতের ভাষায় এটি বলে, ≡ (mod m) একটি equivalence relation: এমন সম্পর্ক যা reflexive, symmetric আর transitive. Relation-এর এই ধর্মগুলো প্রমাণসহ পর্ব ৫২ “Relation আর তার ধর্ম — reflexive, symmetric, transitive”-এ, আর equivalence relation কীভাবে একটি সেটকে partition করে তা পর্ব ৫৫ “Equivalence relation আর partition”-এ আসবে। এখানে congruence-এর জন্য সরাসরি প্রমাণ দেখি।

উপপাদ্য ২.১

প্রতিটি ধনাত্মক m-এর জন্য, সব পূর্ণসংখ্যা a, b, c-তে: (ক) a ≡ a (reflexive); (খ) a ≡ b ⇒ b ≡ a (symmetric); (গ) a ≡ b আর b ≡ c ⇒ a ≡ c (transitive). সবখানে mod m.

প্রমাণ:

  1. (ক) a − a = 0 = 0 · m, তাই m ∣ 0. — ∣-এর সংজ্ঞা, k = 0
  2. (খ) a − b = km হলে b − a = (−k)m. — দুই পাশে −1 দিয়ে গুণ
  3. (গ) a − b = km আর b − c = lm হলে যোগ করে a − c = (k + l)m. ∎ — দুটি সমীকরণ যোগ
সংজ্ঞা ২.২ আর উপপাদ্য ২.৩

a-এর congruence class (residue class) [a]m = {x ∈ ℤ : x ≡ a (mod m)} = {…, a − 2m, a − m, a, a + m, a + 2m, …}. দাবি: ঠিক m-টি আলাদা class আছে, [0], [1], …, [m − 1], আর প্রতিটি পূর্ণসংখ্যা এদের ঠিক একটিতে পড়ে।

প্রমাণ:

  1. প্রতিটি পূর্ণসংখ্যা কোনো একটিতে আছে: যেকোনো x-এর জন্য r = x mod m নিলে 0 ≤ r ≤ m − 1 আর উপপাদ্য ১.২(গ) অনুযায়ী x ≡ r, তাই x ∈ [r]. — division algorithm
  2. এই m-টি class আলাদা: 0 ≤ r < s ≤ m − 1 হলে 0 < s − r < m, তাই m ∤ (s − r), অর্থাৎ r ≢ s আর r ∉ [s]. — m-এর চেয়ে ছোট ধনাত্মক সংখ্যাকে m ভাগ করে না
  3. দুটি class হয় সমান, নয়তো একেবারে আলাদা: z যদি [r] আর [s] দুটিতেই থাকে, তবে z ≡ r আর z ≡ s, তাই symmetric আর transitive ধর্মে r ≡ s, যা ধাপ ২-এর সঙ্গে বিরোধ। ∎ — উপপাদ্য ২.১, contradiction

প্রতিটি class থেকে একটি করে প্রতিনিধি নিলে পাওয়া যায় একটি complete residue system; সবচেয়ে প্রচলিত বাছাই 0, 1, …, m − 1, যাদের বলে least non-negative residue. কিন্তু অন্য বাছাইও বৈধ: mod 7-এ {−3, −2, −1, 0, 1, 2, 3} একটি complete residue system, আর ঋণাত্মক প্রতিনিধি অনেক সময় হিসাব ছোট করে (যেমন 6 ≡ −1). নিচের ছবিতে mod 7-এর ঘড়ি: সাতটি ঘর সাতটি class, আর বাজারঘরের ১১ দিনের ভ্যান-রুট প্রতিবার কাঁটাকে ৪ ঘর এগিয়ে দেয়, কারণ 11 ≡ 4 (mod 7).

mod 7-এর ঘড়ি — সপ্তাহের সাত দিন, সাতটি congruence class

দিন 0 বৃহস্পতিবার ধরে প্রতি ১১ দিনের ডেলিভারি: বৃহস্পতি → সোম → শুক্র → মঙ্গল

The mod 7 clock: weekdays as congruence classes and the 11-day delivery jumps ১ ২ ৩ রবি0 সোম1 মঙ্গল2 বুধ3 বৃহঃ4 শুক্র5 শনি6 [4] = {…, −10, −3, 4, 11, 18, 25, …} প্রতি লাফ +11 দিন ≡ +4 ঘর (mod 7)
সংজ্ঞা ২.৪ (ℤm)

ℤm = {0, 1, …, m − 1}, সঙ্গে দুটি অপারেশন: a +m b = (a + b) mod m আর a ·m b = (a · b) mod m. প্রতিটি সংখ্যা আসলে তার class-এর প্রতিনিধি, তাই ℤm-কে class-গুলোর সেট হিসেবেও ভাবা যায়: [a] + [b] = [a + b], [a] · [b] = [ab].

এখানে একটি সূক্ষ্ম প্রশ্ন লুকিয়ে আছে। [a] + [b] = [a + b] লিখতে গেলে নিশ্চিত হতে হবে যে ফল প্রতিনিধি বাছাইয়ের ওপর নির্ভর করে না: mod 7-এ [4] + [5]-এর জন্য 4 আর 5 নিলে 9 ∈ [2], আর 11 আর −2 নিলেও 9 ∈ [2] পাওয়া চাই। গণিতে একে বলে অপারেশনটি well-defined. পরের অংশের উপপাদ্য ঠিক এই নিশ্চয়তাটুকু দেয়।

৩ · mod m-এ যোগ, বিয়োগ আর গুণ — কোনটি চলে, কোনটি চলে না

উপপাদ্য ৩.১

a ≡ b (mod m) আর c ≡ d (mod m) হলে (ক) a + c ≡ b + d, (খ) a − c ≡ b − d, আর (গ) ac ≡ bd, সবখানে mod m.

প্রমাণ: উপপাদ্য ১.২(খ) অনুযায়ী a = b + km আর c = d + lm কোনো পূর্ণসংখ্যা k, l-এর জন্য।

  1. (ক) a + c = (b + d) + (k + l)m, তাই m ∣ ((a + c) − (b + d)). — যোগ আর বণ্টন
  2. (খ) a − c = (b − d) + (k − l)m. — একইভাবে
  3. (গ) ac = (b + km)(d + lm) = bd + (bl + kd + klm)m. বন্ধনীর রাশি একটি পূর্ণসংখ্যা, তাই ac ≡ bd. ∎ — গুণ খুলে m সাধারণ নেওয়া
অনুসিদ্ধান্ত ৩.২

a ≡ b (mod m) হলে প্রতিটি k ≥ 0-তে ak ≡ bk (mod m). আরও সাধারণভাবে, পূর্ণসংখ্যা সহগের যেকোনো polynomial f-এর জন্য f(a) ≡ f(b) (mod m). বিশেষ করে (a + b) mod m = ((a mod m) + (b mod m)) mod m, আর গুণেও একই।

প্রমাণ: k-এর ওপর mathematical induction (পর্ব ১০)। ভিত্তি: a0 = 1 ≡ 1 = b0. ধাপ: ak ≡ bk ধরে নিলে উপপাদ্য ৩.১(গ)-এ ak · a ≡ bk · b, অর্থাৎ ak+1 ≡ bk+1. Polynomial-এর জন্য প্রতিটি পদ ciai ≡ cibi, তারপর উপপাদ্য ৩.১(ক) দিয়ে পদগুলো যোগ। শেষ দাবিটি এসেছে a ≡ a mod m থেকে। ∎

এর ব্যবহারিক অর্থ বিশাল: হিসাবের যেকোনো মাঝপথে ভাগশেষ নিয়ে সংখ্যা ছোট রাখা যায়, ফল বদলায় না। বাজারঘরের তিনটি অর্ডারের মোট পরিমাণ 98,765 + 43,217 + 55,555 টাকা; মোট পরিমাণ mod 9 জানতে যোগফল না করেও প্রতিটির ভাগশেষ (8, 8, 7) যোগ করে 23 ≡ 5 (mod 9) পাওয়া যায়। কিন্তু দুটি জায়গায় সাধারণ বীজগণিতের অভ্যাস এখানে ভুল করায়।

ফাঁদ ১, কাটাকাটি: 2 · 3 ≡ 2 · 8 (mod 10), কারণ 6 আর 16-এর পার্থক্য 10. কিন্তু দুই পাশ থেকে 2 কেটে 3 ≡ 8 (mod 10) লেখা যায় না: 3 আর 8-এর পার্থক্য 5. ac ≡ bc থেকে a ≡ b পাওয়া যায় শুধু gcd(c, m) = 1 হলে; এর প্রমাণ আর modular inverse পর্ব ২৩ “Linear congruence আর Chinese Remainder Theorem”-এ আসবে।

ফাঁদ ২, ঘাতকে mod m করা: 1 ≡ 4 (mod 3), কিন্তু 21 = 2 আর 24 = 16 ≡ 1 (mod 3). ভিত্তি (base) mod m করা চলে, ঘাত (exponent) চলে না। ঘাত কোন সংখ্যার মডুলোতে ছোট করা যায়, তা পর্ব ২৪ “Fermat-এর ছোট উপপাদ্য আর Euler-এর totient”-এর বিষয়।

নিচের ব্লক ℤ6 আর ℤ7-এর ছক ছাপে, তারপর m = 2 থেকে 12 পর্যন্ত দেখে গুণের ছকের কয়টি সারিতে 1 আছে আর কোথায় zero divisor (এমন দুটি অশূন্য সংখ্যা, যাদের গুণফল 0) আছে। শেষে নির্দিষ্ট seed-এ ১০,০০০টি এলোমেলো উদাহরণে উপপাদ্য ৩.১ পরীক্ষা করে, ঋণাত্মক সংখ্যাসহ। প্রথমবার চালালে ব্রাউজারে Python (Pyodide) নামতে কয়েক সেকেন্ড লাগে।

import random
random.seed(22)

def show(m, op, sym):
    print(f"{sym}_{m} | " + " ".join(str(j) for j in range(m)))
    print("-" * (2 * m + 6))
    for i in range(m):
        print(f"  {i}  | " + " ".join(str(op(i, j) % m) for j in range(m)))

show(6, lambda a, b: a + b, "+")
show(6, lambda a, b: a * b, "·")
show(7, lambda a, b: a * b, "·")

for m in range(2, 13):
    units = [a for a in range(1, m) if any(a * x % m == 1 for x in range(m))]
    zd = [(a, b) for a in range(1, m) for b in range(a, m) if a * b % m == 0]
    print(f"m={m:2}: 1 আছে {len(units):2}/{m-1:2} সারিতে;",
          "শূন্য-ভাজক যেমন", zd[0] if zd else "নেই")

ok = 0                                  # Theorem 3.1 on random data
for _ in range(10000):
    m = random.randint(2, 50)
    a, c = random.randint(-999, 999), random.randint(-999, 999)
    b = a + m * random.randint(-50, 50)     # b ≡ a (mod m)
    d = c + m * random.randint(-50, 50)     # d ≡ c (mod m)
    ok += ((a + c) - (b + d)) % m == 0 and (a * c - b * d) % m == 0
print("a≡b, c≡d ⇒ a+c≡b+d আর ac≡bd:", ok, "of 10000")

আসল আউটপুট (ব্রাউজারের Python), মাঝের দুটি ছক সংক্ষেপে:

+_6 | 0 1 2 3 4 5
------------------
  0  | 0 1 2 3 4 5
  1  | 1 2 3 4 5 0
  …
  5  | 5 0 1 2 3 4
·_6 | 0 1 2 3 4 5
------------------
  0  | 0 0 0 0 0 0
  1  | 0 1 2 3 4 5
  2  | 0 2 4 0 2 4
  3  | 0 3 0 3 0 3
  4  | 0 4 2 0 4 2
  5  | 0 5 4 3 2 1
·_7 | 0 1 2 3 4 5 6
  …
  3  | 0 3 6 2 5 1 4
  …
m= 2: 1 আছে  1/ 1 সারিতে; শূন্য-ভাজক যেমন নেই
m= 3: 1 আছে  2/ 2 সারিতে; শূন্য-ভাজক যেমন নেই
m= 4: 1 আছে  2/ 3 সারিতে; শূন্য-ভাজক যেমন (2, 2)
m= 5: 1 আছে  4/ 4 সারিতে; শূন্য-ভাজক যেমন নেই
m= 6: 1 আছে  2/ 5 সারিতে; শূন্য-ভাজক যেমন (2, 3)
m= 7: 1 আছে  6/ 6 সারিতে; শূন্য-ভাজক যেমন নেই
m= 8: 1 আছে  4/ 7 সারিতে; শূন্য-ভাজক যেমন (2, 4)
m= 9: 1 আছে  6/ 8 সারিতে; শূন্য-ভাজক যেমন (3, 3)
m=10: 1 আছে  4/ 9 সারিতে; শূন্য-ভাজক যেমন (2, 5)
m=11: 1 আছে 10/10 সারিতে; শূন্য-ভাজক যেমন নেই
m=12: 1 আছে  4/11 সারিতে; শূন্য-ভাজক যেমন (2, 6)
a≡b, c≡d ⇒ a+c≡b+d আর ac≡bd: 10000 of 10000

যোগের ছকের প্রতিটি সারি আগের সারিকে এক ঘর বাঁয়ে ঘুরিয়ে পাওয়া, ঘড়ির মতোই। গুণের ছকে ℤ6 আর ℤ7-এর পার্থক্য চোখে পড়ার মতো: ℤ6-এ 2 · 3 = 0, যদিও কোনোটিই 0 নয়, আর 2, 3, 4-এর সারিতে 1 নেই। ℤ7-এ প্রতিটি অশূন্য সারি 1 থেকে 6-এর একটি পুনর্বিন্যাস (permutation), যেমন 3-এর সারি 3 6 2 5 1 4. তালিকায় দেখা যাচ্ছে, যে m-গুলোতে শূন্য-ভাজক নেই আর সব সারিতে 1 আছে, সেগুলো ঠিক 2, 3, 5, 7, 11, অর্থাৎ মৌলিক সংখ্যা। কোন সারিতে 1 থাকে (inverse) তার পূর্ণ উত্তর আর প্রমাণ পর্ব ২৩-এ, আর “সারিটি একটি permutation” এই পর্যবেক্ষণই পর্ব ২৪-এ Fermat-এর উপপাদ্যের প্রমাণের চাবি। শেষ লাইনে উপপাদ্য ৩.১ ১০,০০০টি ক্ষেত্রেই টিকেছে, ঋণাত্মক a, c-সহ।

৪ · বিভাজ্যতার পরীক্ষা — 3, 9 আর 11 কেন কাজ করে

স্কুলে শেখা নিয়ম: “অঙ্কগুলোর যোগফল 9 দিয়ে বিভাজ্য হলে সংখ্যাটিও 9 দিয়ে বিভাজ্য।” কিন্তু কেন? অনুসিদ্ধান্ত ৩.২ এর পুরো ব্যাখ্যা দেয়, আর একই যুক্তি 11-এর পরীক্ষাও দেয়। এগুলোকে বলে divisibility test.

উপপাদ্য ৪.১

n-এর দশমিক অঙ্কগুলো ডান থেকে d0, d1, …, dk, অর্থাৎ n = dk10k + ⋯ + d110 + d0. তাহলে

n ≡ d0 + d1 + ⋯ + dk (mod 9) আর n ≡ d0 − d1 + d2 − ⋯ + (−1)kdk (mod 11)

প্রথমটি mod 3-এও সত্য। তাই 3 বা 9 ∣ n ঠিক তখন, যখন অঙ্কের যোগফল যথাক্রমে 3 বা 9 দিয়ে বিভাজ্য; আর 11 ∣ n ঠিক তখন, যখন একান্তর চিহ্নে নেওয়া যোগফল (alternating digit sum) 11 দিয়ে বিভাজ্য।

প্রমাণ:

  1. 10 − 1 = 9, তাই 10 ≡ 1 (mod 9). অনুসিদ্ধান্ত ৩.২-এ প্রতিটি i-তে 10i ≡ 1i = 1 (mod 9). — ঘাতে congruence টিকে থাকে
  2. উপপাদ্য ৩.১-এ di10i ≡ di, আর সব পদ যোগ করে n ≡ ∑ di (mod 9). 3 ∣ 9, তাই mod 9-এর যেকোনো congruence mod 3-এও সত্য, ফলে mod 3-এর দাবিও আসে। — উপপাদ্য ৩.১, আর 3 ∣ 9
  3. 10 − (−1) = 11, তাই 10 ≡ −1 (mod 11), আর 10i ≡ (−1)i. একইভাবে n ≡ ∑ (−1)idi (mod 11). — অনুসিদ্ধান্ত ৩.২
  4. n ≡ s (mod m) হলে m ∣ n ⇔ m ∣ s, কারণ n − s m-এর গুণিতক। ∎ — উপপাদ্য ১.২

লক্ষ করুন, উপপাদ্যটি শুধু “বিভাজ্য কি না” নয়, ভাগশেষও দেয়: 918082-এর অঙ্কের যোগফল 28, আর 28 ≡ 1 (mod 9), তাই 918082 mod 9 = 1. ভাগশেষের এই রূপটিই পুরোনো “নয় বাদ দেওয়া” (casting out nines) পদ্ধতির ভিত্তি: হাতে করা গুণ বা যোগ মেলানোর জন্য দুই পাশের অঙ্ক-যোগফল mod 9 মেলানো। নিচের stepper একই সংখ্যায় 11-এর পরীক্ষা ধাপে ধাপে দেখায়।

1n = 918082210 ≡ −1 (mod 11)310^i ≡ (−1)^i4অঙ্ক (ডান থেকে): 2 8 0 8 1 95চিহ্ন:            + − + − + −6n ≡ 2−8+0−8+1−9 = −227mod 9: অঙ্ক-যোগ 28 ≡ 1
এই ধাপের হিসাব
রূপ
ফল
  1. রূপ। 918082 = 9·105 + 1·104 + 8·103 + 0·102 + 8·10 + 2. দশমিক লেখার অর্থই এই। ডান থেকে অঙ্কগুলো d0 = 2, d1 = 8, d2 = 0, d3 = 8, d4 = 1, d5 = 9.
  2. চাবি। 10 − (−1) = 11, আর 11 ∣ 11, তাই সংজ্ঞা অনুযায়ী 10 ≡ −1 (mod 11). পুরো পরীক্ষা এই একটি congruence-এর ওপর দাঁড়িয়ে।
  3. ঘাত। অনুসিদ্ধান্ত ৩.২ দিয়ে 10i ≡ (−1)i: জোড় i-তে +1, বিজোড় i-তে −1. যেমন 102 = 100 = 9 · 11 + 1, আর 103 = 1000 = 91 · 11 − 1.
  4. চিহ্ন বসানো। উপপাদ্য ৩.১ দিয়ে প্রতিটি di10i-কে ±di দিয়ে বদলানো যায়: +2 −8 +0 −8 +1 −9. ডান দিকের অঙ্ক থেকে + দিয়ে শুরু করতে হয়।
  5. যোগ। 2 − 8 + 0 − 8 + 1 − 9 = −22 = −2 · 11 ≡ 0 (mod 11). তাই 11 ∣ 918082. সত্যিই 918082 = 11 × 83462, নিচের ব্লক এটি যাচাই করে।
  6. বোনাস: mod 9. 10 ≡ 1 (mod 9) বলে চিহ্ন সব +: 9 + 1 + 8 + 0 + 8 + 2 = 28 ≡ 1 (mod 9). তাই 918082 mod 9 = 1, আর 918082 না 9, না 3 দিয়ে বিভাজ্য। একই কৌশল, শুধু 10-এর ভাগশেষ আলাদা। ∎

একই ধারণা 7 আর 13-এর জন্যও একটি পরীক্ষা দেয়, শুধু একক অঙ্কের বদলে তিন অঙ্কের দলে। 1001 = 7 · 11 · 13, তাই 1000 ≡ −1 (mod 7), (mod 11) আর (mod 13). ফলে সংখ্যাটিকে ডান থেকে তিন অঙ্ক করে দলে ভাগ করে একান্তর চিহ্নে যোগ করলে যে সংখ্যা আসে, তা মূল সংখ্যার সঙ্গে এই তিনটি modulus-এই congruent. যেমন 135792468: 468 − 792 + 135 = −189 = −27 · 7, তাই 7 ∣ 135792468. নিচের ব্লক এই সব পরীক্ষা অঙ্ক থেকে (সংখ্যাটিকে string হিসেবে পড়ে) চালায় আর হাজার হাজার সংখ্যায় সরাসরি %-এর সঙ্গে মেলায়।

import random
random.seed(22)

def digits(n):                     # least significant digit first
    return [int(c) for c in reversed(str(n))]

def alt_sum(n):                    # d0 - d1 + d2 - d3 + ...
    return sum(d if k % 2 == 0 else -d for k, d in enumerate(digits(n)))

def test3(n):  return sum(digits(n)) % 3 == 0
def test9(n):  return sum(digits(n)) % 9 == 0
def test11(n): return alt_sum(n) % 11 == 0

def group_sum(n):                  # 1000 ≡ -1 (mod 7, 11, 13)
    s, sign = 0, 1
    while n:
        s, n, sign = s + sign * (n % 1000), n // 1000, -sign
    return s

n = 918082
print(n, ": digit sum", sum(digits(n)), " alternating sum", alt_sum(n))
print("test3/test9/test11:", test3(n), test9(n), test11(n), "  918082 // 11 =", n // 11)
n = 135792468
print(n, ": 3-digit group sum", group_sum(n), "  % 7 =", group_sum(n) % 7, "  % 13 =", group_sum(n) % 13)

nums = [random.randrange(1, 10**12) for _ in range(20000)] + list(range(1, 3000))
bad = dict.fromkeys(["3", "9", "11", "7", "13"], 0)
for x in nums:
    bad["3"] += test3(x) != (x % 3 == 0)
    bad["9"] += test9(x) != (x % 9 == 0)
    bad["11"] += test11(x) != (x % 11 == 0)
    for d in (7, 13):
        bad[str(d)] += (group_sum(x) % d == 0) != (x % d == 0)
print(len(nums), "numbers checked; mismatches:", bad)

আসল আউটপুট (ব্রাউজারের Python):

918082 : digit sum 28  alternating sum -22
test3/test9/test11: False False True   918082 // 11 = 83462
135792468 : 3-digit group sum -189   % 7 = 0   % 13 = 6
22999 numbers checked; mismatches: {'3': 0, '9': 0, '11': 0, '7': 0, '13': 0}

প্রথম দুই লাইন stepper-এর ফল মেলায়: অঙ্ক-যোগফল 28 (তাই 3 বা 9 দিয়ে নয়), একান্তর যোগফল −22 (তাই 11 দিয়ে), আর ভাগফল 83462. তৃতীয় লাইনে তিন অঙ্কের দলের যোগফল −189: 7 দিয়ে বিভাজ্য, 13 দিয়ে নয় (ভাগশেষ 6). শেষ লাইনে ২২,৯৯৯টি সংখ্যার (১ থেকে ২,৯৯৯ সব, আর ১২ অঙ্ক পর্যন্ত ২০,০০০টি এলোমেলো) একটিতেও অঙ্ক-ভিত্তিক পরীক্ষা আর সরাসরি % দ্বিমত করেনি। খেয়াল করুন, group_sum একটি বড় সংখ্যাকে অনেক ছোট সংখ্যায় নামিয়ে আনে, কিন্তু শেষ ধাপে তবুও একটি ছোট ভাগ লাগে; হাতে হিসাবের সময় এই সংকোচনই আসল সুবিধা।

৫ · Modular exponentiation — বারবার বর্গ করে বিশাল ঘাত

বাজারঘরের নিরাপত্তা দল জানতে চায় 7222 mod 1000. সরল পথ: 7222 পুরোটা হিসাব করে 1000 দিয়ে ভাগ। কিন্তু 7222 একটি ১৮৮ অঙ্কের সংখ্যা, আর cryptography-তে ঘাত হয় শত শত অঙ্কের, যেখানে পুরো সংখ্যাটি লেখার মতো জায়গা মহাবিশ্বেও নেই। অনুসিদ্ধান্ত ৩.২ বলে প্রতিটি গুণের পরেই mod 1000 নেওয়া যায়, তাই সংখ্যা কখনো 1000 × 1000-এর বেশি হয় না। বাকি সমস্যা গুণের সংখ্যা: 221টি গুণ ছোট ঘাতে চলে, কিন্তু 10100 ঘাতে অসম্ভব। সমাধান পর্ব ১৩-এ দেখা binary exponentiation, প্রতিটি ধাপে mod নিয়ে। একে বলে modular exponentiation, আর পদ্ধতিটির প্রচলিত নাম repeated squaring বা square-and-multiply.

ধরুন একটি গুজব প্রতিদিন দ্বিগুণ মানুষের কাছে পৌঁছায়। ১ জন থেকে ১,০০০ জন ছাড়াতে মাত্র ১০ দিন লাগে (210 = 1024), অথচ রোজ একজন করে বাড়লে লাগত ৯৯৯ দিন। বারবার বর্গ করাও তাই: a, a2, a4, a8, … প্রতিটি গুণে ঘাত দ্বিগুণ হয়, তাই b পর্যন্ত পৌঁছাতে প্রায় log2 b ধাপই যথেষ্ট।

উপপাদ্য ৫.১

a একটি পূর্ণসংখ্যা, b ≥ 1, m ≥ 2. b-এর binary রূপ ধরে বারবার বর্গ করে (প্রতিটি গুণের পরে mod m নিয়ে) ab mod m বের করতে বড়জোর 2⌊log2 b⌋টি গুণ লাগে, আর প্রতিটি গুণের দুই উপাদানই m-এর চেয়ে ছোট অঋণাত্মক সংখ্যা।

প্রমাণ: L = ⌊log2 b⌋, অর্থাৎ b-এর binary রূপে L + 1টি অঙ্ক: b = ∑ 2i (যেসব i-তে bit 1), 0 ≤ i ≤ L.

  1. x0 = a mod m, আর xi = xi−12 mod m (1 ≤ i ≤ L). Induction-এ xi ≡ a2i (mod m), কারণ (a2i−1)2 = a2i. এখানে ঠিক L-টি বর্গ। — অনুসিদ্ধান্ত ৩.২, ঘাতের নিয়ম
  2. ঘাতের নিয়মে ab = ∏ a2i, গুণফল নেওয়া হয় যেসব i-তে bit 1. উপপাদ্য ৩.১(গ) দিয়ে ab ≡ ∏ xi (mod m). — as+t = asat
  3. b-তে 1-bit আছে বড়জোর L + 1টি, তাই এদের গুণফলে বড়জোর L-টি গুণ (k-টি সংখ্যা গুণ করতে k − 1টি গুণ লাগে)। — গোনা
  4. মোট বড়জোর L + L = 2⌊log2 b⌋. প্রতিবার গুণের পরে mod m নেওয়া হয়, তাই প্রতিটি উপাদান 0 থেকে m − 1-এর মধ্যে, আর গুণফল m2-এর কম। ∎

লুপের সঠিকতা, অর্থাৎ invariant r · xe ≡ ab, পর্ব ১৩-এ 313-এ প্রমাণ করা হয়েছে; mod নেওয়াতে প্রমাণে শুধু “=”-এর জায়গায় “≡” বসে। 10100-এর মতো ঘাতে L = 332, তাই বড়জোর ৬৬৪টি গুণ। নিচের stepper একটি ছোট উদাহরণ দেখায়: 3200 mod 13.

1200 = 11001000 (binary)2    = 128 + 64 + 83x_i = x_(i−1)² mod 134bit 1: r = r · x_i mod 1353^200 mod 13 = r6যাচাই: 3³ = 27 ≡ 1
x_i = 3^(2^i) mod 13
i
x_i
r
গুণ গোনা
  1. Binary রূপ। 200 = 128 + 64 + 8, অর্থাৎ binary 11001000. তাই 3200 = 3128 · 364 · 38. L = ⌊log2 200⌋ = 7.
  2. বারবার বর্গ। x0 = 3, x1 = 9, x2 = 81 mod 13 = 3, তারপর আবার 9, 3, … কারণ 92 = 81 = 6 · 13 + 3. সাতটি বর্গে x0 থেকে x7: 3 9 3 9 3 9 3 9. কোনো সংখ্যা 12-এর বেশি হয়নি।
  3. বাছাই। যেসব i-তে bit 1: i = 3 (8), i = 6 (64), i = 7 (128). সংশ্লিষ্ট মান x3 = 9, x6 = 3, x7 = 9.
  4. প্রথম গুণ। 9 · 3 = 27 = 2 · 13 + 1, তাই r = 1. এখন পর্যন্ত 7টি বর্গ আর 1টি গুণ।
  5. দ্বিতীয় গুণ। 1 · 9 = 9, তাই 3200 mod 13 = 9. মোট 9টি গুণ, উপপাদ্যের সীমা 2 · 7 = 14-এর মধ্যে; সরল পথে লাগত 199টি।
  6. অন্য পথে যাচাই। 33 = 27 ≡ 1 (mod 13). 200 = 3 · 66 + 2, তাই 3200 = (33)66 · 32 ≡ 166 · 9 = 9. একই উত্তর। এমন ছোট চক্র খুঁজে পাওয়া সবসময় সহজ নয়, তাই সাধারণ পদ্ধতি repeated squaring; চক্রের তত্ত্ব পর্ব ২৪-এ। ∎

এবার মূল প্রশ্ন, 7222 mod 1000. নিচের ব্লক ডান থেকে বাঁয়ে bit পড়ে, প্রতিটি ধাপের e, bit, x আর r ছাপে, আর গুণ গোনে। প্রথম গুণটি (1 দিয়ে) গোনা হয়নি আর শেষের অপ্রয়োজনীয় বর্গটি করাই হয়নি, যাতে সংখ্যাটি উপপাদ্যের হিসাবের সঙ্গে মেলে। তারপর Python-এর নিজস্ব pow(a, b, m)-এর সঙ্গে মেলায়। Python-এর int যেকোনো আকারের হতে পারে, তাই 7222 পুরোটাও হিসাব করে দেখা যায়।

def pow_mod_trace(a, b, m):
    """right-to-left repeated squaring; prints one row per bit of b"""
    r, x, e = 1, a % m, b
    sq = mul = 0
    started = False
    print("   e  bit  x = a^(2^i) mod m    r")
    while e > 0:
        bit = e & 1
        if bit:
            mul += started          # the first factor needs no multiplication
            r, started = r * x % m, True
        print(f"{e:4}   {bit}   {x:6}            {r:4}")
        e >>= 1
        if e:                       # the last square is never needed
            x, sq = x * x % m, sq + 1
    return r, sq, mul

r, sq, mul = pow_mod_trace(7, 222, 1000)
print("7^222 mod 1000 =", r, "   pow(7, 222, 1000) =", pow(7, 222, 1000))
print("squarings:", sq, " multiplications:", mul, " total:", sq + mul,
      " (naive: 221)")
big = 7 ** 222
print("7^222 itself has", len(str(big)), "digits, ends in", str(big)[-3:])

b = 10 ** 100                       # a googol as the exponent
print("bits in b:", b.bit_length(), "  7^b mod 1000 =", pow(7, b, 1000))

আসল আউটপুট (ব্রাউজারের Python):

   e  bit  x = a^(2^i) mod m    r
 222   0        7               1
 111   1       49              49
  55   1      401             649
  27   1      801             849
  13   1      601             249
   6   0      201             249
   3   1      401             849
   1   1      801              49
7^222 mod 1000 = 49    pow(7, 222, 1000) = 49
squarings: 7  multiplications: 5  total: 12  (naive: 221)
7^222 itself has 188 digits, ends in 049
bits in b: 333   7^b mod 1000 = 1

প্রতিটি সারিতে e অর্ধেক হয়, আর e-এর শেষ bit (বিজোড় হলে 1) ঠিক করে x-কে r-এ গুণ করা হবে কি না। 222 = 110111102, তাই ছয়টি 1-bit, পাঁচটি গুণ আর সাতটি বর্গ: মোট ১২, উপপাদ্যের সীমা 14-এর মধ্যে, আর সরল পথের 221-এর তুলনায় প্রায় আঠারো ভাগের এক ভাগ। সারণির পরের তৃতীয় লাইনে মিলিয়ে দেখা গেল: ১৮৮ অঙ্কের পুরো সংখ্যাটির শেষ তিন অঙ্ক সত্যিই 049. শেষ লাইনে ঘাত একটি googol (10100), যার binary রূপে ৩৩৩টি অঙ্ক; pow চোখের পলকে উত্তর দেয়, কারণ গুণ লাগে কয়েকশো, 10100টি নয়। উত্তর 1 আসার কারণ: 720 ≡ 1 (mod 1000) আর 20 ∣ 10100; এই ধরনের চক্রের তত্ত্ব পর্ব ২৪-এর বিষয়।

বাস্তবে: RSA আর Diffie–Hellman-এর মূল গণনা ঠিক এই modular exponentiation, শত শত অঙ্কের সংখ্যায়; তাদের গণিত পর্ব ২৮ আর ২৯-এ আসবে। ছোট সংখ্যার textbook RSA শুধু বোঝার জন্য, নিরাপদ নয়: বাস্তব ব্যবস্থায় padding, বড় key আর যাচাই করা লাইব্রেরি লাগে (সাইবার সিকিউরিটি কোর্সের পর্ব ১০ দেখুন)। Contest-এ pow আর modular arithmetic-এর কোড-কৌশলের জন্য DSA কোর্সের পর্ব ৬২ দেখুন।

৬ · শেষ অঙ্ক, সপ্তাহের দিন আর বাজারঘরের ডেলিভারি-চক্র

“72026-এর শেষ অঙ্ক কত?” এই প্রশ্নের মানে 72026 mod 10. শেষ দুই অঙ্ক মানে mod 100. এমন প্রশ্নে সাধারণত একটি চক্র খুঁজলেই হয়: 71 = 7, 72 = 49, 73 = 343, 74 = 2401. শেষ অঙ্ক 7, 9, 3, 1, তারপর আবার 7. আরও ভালো, 74 = 2401 ≡ 1 (mod 100). তাই 2026 = 4 · 506 + 2 থেকে

72026 = (74)506 · 72 ≡ 1506 · 49 = 49 (mod 100)

অর্থাৎ শেষ দুই অঙ্ক 49, শেষ অঙ্ক 9. যুক্তির প্রতিটি ধাপ অনুসিদ্ধান্ত ৩.২: 74 ≡ 1 হলে তার যেকোনো ঘাতও ≡ 1. লক্ষ করুন, এখানে ঘাত 2026-কে 4 দিয়ে ভাগ করা হয়েছে, কারণ 74 ≡ 1; এটি “ঘাত mod m” নয়, যা §৩-এর ফাঁদ ২-এ নিষেধ করা হয়েছে।

সপ্তাহের দিনের অঙ্কে modulus 7. দিনগুলোকে রবি = 0, সোম = 1, …, শনি = 6 সংখ্যা দিলে “আজ d বার, n দিন পরে কী বার?” প্রশ্নের উত্তর (d + n) mod 7. আজ বৃহস্পতিবার (4) হলে ১০০ দিন পরে (4 + 100) mod 7 = 104 mod 7 = 6, শনিবার; কারণ 100 = 14 · 7 + 2. দশ লক্ষ দিন পরে? 106 mod 7 হিসাবে 10 ≡ 3, 102 ≡ 2, 103 ≡ 6 ≡ −1, তাই 106 ≡ (−1)2 = 1, আর উত্তর শুক্রবার। বাজারঘরের ১১ দিনের ভ্যান-রুটের প্রশ্নগুলো নিচের stepper-এ।

1রবি=0 সোম=1 … শনি=62দিন 0 = বৃহস্পতি = 4311 ≡ 4 (mod 7)4k-তম ডেলিভারি: (4 + 4k) mod 754k ≡ 0 (mod 7) ⇒ 7 ∣ k6lcm(7, 11) = 77
day = 11k, বার = (4 + 11k) mod 7
k
দিন নম্বর
হিসাব
বার
  1. সংকেত। সাতটি বার ℤ7-এর সাতটি সংখ্যা: রবি = 0, …, বৃহস্পতি = 4, শনি = 6. ঢাকা–সিলেট ভ্যান প্রথম যায় দিন 0-তে, একটি বৃহস্পতিবারে, তারপর প্রতি ১১ দিনে।
  2. লাফের মাপ। 11 = 7 + 4, তাই 11 ≡ 4 (mod 7). প্রতিটি ডেলিভারি বারকে ঘড়িতে ৪ ঘর এগিয়ে দেয়। k-তম ডেলিভারি (দিন 11k) পড়ে (4 + 4k) mod 7 বারে।
  3. প্রথম দুটি। k = 1: 4 + 4 = 8 ≡ 1, সোমবার। k = 2: 4 + 8 = 12 ≡ 5, শুক্রবার। ক্যালেন্ডারে গুনে মিলিয়ে নিতে পারেন: বৃহস্পতিবারের ১১ দিন পরে সোমবার।
  4. ২০তম ডেলিভারি। দিন 220. 4 + 4 · 20 = 84 = 12 · 7, তাই ≡ 0: রবিবার। 220 পর্যন্ত গোনার দরকার হয়নি, শুধু 20-কে 4 দিয়ে গুণ আর mod 7.
  5. আবার কবে বৃহস্পতিবার? দরকার 4k ≡ 0 (mod 7), অর্থাৎ 7 ∣ 4k. 7 মৌলিক আর 7 ∤ 4, তাই 7 ∣ k (Euclid-এর lemma, পর্ব ২১)। সবচেয়ে ছোট k = 7, দিন 77: 4 + 28 = 32 ≡ 4, বৃহস্পতিবার।
  6. দুই রুটের দেখা। প্রতি বৃহস্পতিবার একটি সাপ্তাহিক ভ্যানও যায় (দিন 0, 7, 14, …)। দুটি একই দিনে পড়ে যখন 7 ∣ d আর 11 ∣ d, অর্থাৎ d হলো 77-এর গুণিতক: 77, 154, 231. 77 = lcm(7, 11), যার তত্ত্ব পর্ব ২১-এ। ∎

নিচের ব্লক এই অংশের সব দাবি এক জায়গায় পরীক্ষা করে: শেষ অঙ্কের চক্র, 72026-এর শেষ দুই অঙ্ক, stepper-এর বারগুলো, আর §৭-এর একটি প্রমাণের দাবি ছোট n-এ।

print("7^k mod 10, k = 1..8:", [pow(7, k, 10) for k in range(1, 9)])
print("7^2026: mod 10 =", pow(7, 2026, 10), "  mod 100 =", pow(7, 2026, 100))
print("2^100 mod 7 =", pow(2, 100, 7), "   3^1000 mod 100 =", pow(3, 1000, 100))

days = ["রবি", "সোম", "মঙ্গল", "বুধ", "বৃহস্পতি", "শুক্র", "শনি"]
today = 4                                  # day 0 is a Thursday
for k in [0, 1, 2, 3, 7, 20]:
    print(f"delivery {k:2}: day {11 * k:3} -> {days[(today + 11 * k) % 7]}")
again = [k for k in range(1, 30) if 11 * k % 7 == 0]
print("same weekday again at k =", again, "-> every", 11 * again[0], "days")
meet = [d for d in range(1, 250) if d % 7 == 0 and d % 11 == 0]
print("weekly and 11-day routes meet on days", meet)
print("10**6 days after Thursday:", days[(today + 10**6) % 7])

claim = all((3**(2*n + 1) + 2**(n + 2)) % 7 == 0 for n in range(500))
print("7 | 3^(2n+1) + 2^(n+2) for n = 0..499:", claim)
print("n^2 mod 4 over n = 0..999:", sorted({n * n % 4 for n in range(1000)}))

আসল আউটপুট (ব্রাউজারের Python):

7^k mod 10, k = 1..8: [7, 9, 3, 1, 7, 9, 3, 1]
7^2026: mod 10 = 9   mod 100 = 49
2^100 mod 7 = 2    3^1000 mod 100 = 1
delivery  0: day   0 -> বৃহস্পতি
delivery  1: day  11 -> সোম
delivery  2: day  22 -> শুক্র
delivery  3: day  33 -> মঙ্গল
delivery  7: day  77 -> বৃহস্পতি
delivery 20: day 220 -> রবি
same weekday again at k = [7, 14, 21, 28] -> every 77 days
weekly and 11-day routes meet on days [77, 154, 231]
10**6 days after Thursday: শুক্র
7 | 3^(2n+1) + 2^(n+2) for n = 0..499: True
n^2 mod 4 over n = 0..999: [0, 1]

শেষ অঙ্কের চক্রের দৈর্ঘ্য 4, আর 72026-এর শেষ দুই অঙ্ক 49, হাতের হিসাবের সঙ্গে মেলে। ডেলিভারির বারগুলো stepper-এর সঙ্গে আর ছবির তীরগুলোর সঙ্গে (বৃহস্পতি → সোম → শুক্র → মঙ্গল) মেলে, আর প্রতি ৭টি ডেলিভারিতে বৃহস্পতিবার ফেরে। শেষ দুই লাইন §৭-এর অঙ্ক ৩ আর অনুশীলন ১-এর দাবি ছোট n-এ পরীক্ষা করে। মনে রাখবেন, ৫০০টি n-এ সত্য হওয়া প্রমাণ নয়; প্রমাণ আসে congruence-এর যুক্তি থেকে।

৭ · পরীক্ষার অঙ্ক, একটি ভুল প্রমাণ আর প্রয়োগের ঝলক

বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর লিখিত পরীক্ষায় এই বিষয়ের যে ধাঁচের অঙ্ক আসে, তার চারটি পূর্ণ সমাধান। প্রতিটি সংখ্যা উপরের ব্লকে বা scratch-এ Python দিয়ে মেলানো।

অঙ্ক ১: 2100-কে 7 দিয়ে ভাগ করলে ভাগশেষ কত?

  1. ছোট ঘাত দেখি: 23 = 8 = 7 + 1, তাই 23 ≡ 1 (mod 7).
  2. 100 = 3 · 33 + 1, তাই 2100 = (23)33 · 2 ≡ 133 · 2 = 2 (mod 7). উত্তর 2.

অঙ্ক ২: 57x23 একটি পাঁচ অঙ্কের সংখ্যা, x একটি অঙ্ক। এটি 9 দিয়ে বিভাজ্য হলে x কত? তখন সংখ্যাটি 11 দিয়েও বিভাজ্য কি?

  1. অঙ্কের যোগফল 5 + 7 + x + 2 + 3 = 17 + x. 9 ∣ (17 + x) আর 0 ≤ x ≤ 9 থেকে 17 + x = 18, তাই x = 1. সংখ্যাটি 57123 = 9 × 6347.
  2. একান্তর যোগফল, ডান থেকে: 3 − 2 + 1 − 7 + 5 = 0, তাই 11 ∣ 57123 (57123 = 11 × 5193). 9 আর 11-এর কোনো সাধারণ ভাজক নেই, তাই 99 ∣ 57123: 57123 = 99 × 577 (এই শেষ ধাপের যুক্তি পর্ব ২১-এর gcd থেকে)।

অঙ্ক ৩: প্রমাণ করুন, প্রতিটি n ≥ 0-তে 7 ∣ 32n+1 + 2n+2

  1. 32n+1 = 3 · 9n, আর 9 ≡ 2 (mod 7), তাই অনুসিদ্ধান্ত ৩.২-এ 32n+1 ≡ 3 · 2n. — ভিত্তি mod 7 করা
  2. 2n+2 = 4 · 2n. যোগ করে 32n+1 + 2n+2 ≡ 3 · 2n + 4 · 2n = 7 · 2n ≡ 0 (mod 7). ∎ — উপপাদ্য ৩.১

একই দাবি induction দিয়েও প্রমাণ করা যায়, কিন্তু congruence-এর পথ দুই লাইনের। §৬-এর ব্লক n = 0 থেকে 499-এ দাবিটি পরীক্ষা করেছে।

অঙ্ক ৪: 7222 mod 1000 repeated squaring দিয়ে, আর কয়টি গুণ লাগল?

222 = 110111102. বর্গগুলো mod 1000: x0 … x7 = 7, 49, 401, 801, 601, 201, 401, 801. 1-bit যেখানে: i = 1, 2, 3, 4, 6, 7. ক্রমে গুণ: 49 · 401 ≡ 649, · 801 ≡ 849, · 601 ≡ 249, · 401 ≡ 849, · 801 ≡ 49. উত্তর 49; বর্গ ৭টি, গুণ ৫টি, মোট ১২ (§৫-এর ব্লকের সারণির সঙ্গে প্রতিটি মান মেলে)।

প্রমাণের ভুল ধরুন: “mod m-এ কাটাকাটি সবসময় চলে”

একজন লিখলেন: “দাবি: ac ≡ bc (mod m) আর c ≢ 0 (mod m) হলে a ≡ b (mod m). প্রমাণ: ac ≡ bc মানে m ∣ (a − b)c. c ≢ 0, তাই দুই পাশকে c দিয়ে ভাগ করলে m ∣ (a − b). ∎” পড়ে ভুলটি নিজে খুঁজুন।

ভুল কোথায়: “m ∣ (a − b)c থেকে c দিয়ে ভাগ করে m ∣ (a − b)” ধাপটি অবৈধ। m-এর কিছু মৌলিক উৎপাদক c-এর ভেতরে থাকতে পারে। Counterexample: m = 10, c = 2, a = 3, b = 8: 10 ∣ (3 − 8) · 2 = −10, কিন্তু 10 ∤ −5. সঠিক দাবি: gcd(c, m) = 1 হলে a ≡ b (mod m); সাধারণভাবে a ≡ b (mod m/gcd(c, m)). উদাহরণে m/gcd = 5, আর সত্যিই 3 ≡ 8 (mod 5). প্রমাণ পর্ব ২৩-এ। শিক্ষা: “ভাগ করা” ধাপ দেখলেই জিজ্ঞেস করুন, কী দিয়ে ভাগ, আর সেটি কেন বৈধ।

প্রয়োগের ঝলক

এই পর্বের নিয়মগুলো বাজারঘরের তিনটি কাজের সরাসরি ভিত্তি, যাদের বিস্তারিত পর্ব ২৬ “সংখ্যাতত্ত্বের প্রয়োগ — check digit, hashing আর pseudorandom সংখ্যা”-এ আসবে। (১) Hash function h(k) = k mod m গ্রাহকের id-কে m-টি তাকের একটিতে পাঠায়, আর m কেমন হলে id-গুলো সমানভাবে ছড়ায়, তার উত্তর congruence class থেকেই আসে (বাস্তবায়ন DSA কোর্সের পর্ব ৩২-এ)। (২) Invoice-এর check digit একটি weighted যোগফল mod 10 বা mod 11, যা §৪-এর পরীক্ষার মতোই টাইপের ভুল ধরে। (৩) Linear congruential generator xn+1 = (axn + c) mod m নামের সূত্র এলোমেলো-দেখতে সংখ্যা বানায়, ঘড়ির কাঁটা ঘোরার মতো একটি চক্রে।

আরও গভীরে: ℤm যোগ আর গুণে একটি commutative ring, আর m মৌলিক হলে প্রতিটি অশূন্য উপাদানের inverse থাকে, তাই ℤp একটি field; পর্ব ৩০ “Finite field আর elliptic curve-এর পরিচয়”-এ এর ব্যবহার আসবে। সংখ্যাতত্ত্বে ≡ চিহ্নটি Carl Friedrich Gauss তাঁর ১৮০১ সালের Disquisitiones Arithmeticae বইয়ে চালু করেন, আর বইটি দিয়েই congruence-কে কেন্দ্র করে আধুনিক সংখ্যাতত্ত্বের যাত্রা ধরা হয়। বড় সংখ্যায় দ্রুত modular গুণের জন্য বাস্তব লাইব্রেরি Montgomery multiplication-এর মতো কৌশল ব্যবহার করে, যা প্রতিটি ধাপে আসল ভাগ এড়িয়ে চলে।

পরীক্ষা ও ইন্টারভিউতে: বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন প্রায়ই আসে:

  • −17 mod 5 কত? 3, কারণ −17 = (−4) · 5 + 3; C-তে -17 % 5 দেয় −2.
  • 3200 mod 13? 9: 33 ≡ 1, আর 200 = 3 · 66 + 2.
  • কোনো বর্গসংখ্যা কি mod 4-এ 2 বা 3 হতে পারে? না; n জোড় হলে n2 ≡ 0, বিজোড় হলে n2 ≡ 1.
  • ab mod m-এর জন্য কয়টি গুণ লাগে? Repeated squaring-এ বড়জোর 2⌊log2 b⌋, অর্থাৎ O(log b).
  • ac ≡ bc (mod m) থেকে a ≡ b কখন? gcd(c, m) = 1 হলে; যেমন 2 · 3 ≡ 2 · 8 (mod 10) কিন্তু 3 ≢ 8.

৮ · AI-এর যুগে — modular হিসাব আর প্রমাণ যাচাই

AI সহকারী “3200 mod 13” বা “শেষ দুই অঙ্ক কত” প্রশ্নের উত্তর আত্মবিশ্বাসের সঙ্গে দেয়, আর প্রায়ই ঠিকও দেয়। কিন্তু বড় সংখ্যায় মাঝের একটি ভাগশেষ ভুল হলেই পুরো উত্তর ভুল, আর ভুল উত্তরও একই আত্মবিশ্বাসে আসে। কিছু ভুল নিয়মিত দেখা যায়:

AI-এর ভুলকীভাবে ধরবেন
বড় ঘাতের মাঝপথে একটি ভাগশেষ ভুল, ফলে চূড়ান্ত উত্তর ভুলPython-এ pow(a, b, m) চালিয়ে মেলান; এক লাইনের কাজ
ঘাতকে mod m করে ফেলা: “2100 mod 7 = 2100 mod 7 = 22 = 4”ঘাত শুধু এমন n দিয়ে কমানো যায়, যেখানে an ≡ 1; ছোট উদাহরণে (1 ≡ 4 mod 3) পরীক্ষা করুন
শর্ত ছাড়া কাটাকাটি: ac ≡ bc থেকে a ≡ bgcd(c, m) = 1 কি না জিজ্ঞেস করুন; 2 · 3 ≡ 2 · 8 (mod 10) দিয়ে পরীক্ষা
ঋণাত্মক সংখ্যার ভাগশেষে ভাষা গুলিয়ে ফেলা (Python-এর 3 বনাম C-র −2)প্রশ্নে কোন সংজ্ঞা চাওয়া হয়েছে তা স্পষ্ট করুন; দুই ভাষায় চালিয়ে দেখুন
বিভাজ্যতার পরীক্ষায় চিহ্ন বাঁ দিক থেকে শুরু করা, বা 11-এর পরীক্ষায় 9-এর নিয়ম লাগানোএকটি জানা গুণিতক (যেমন 121 বা 918082) দিয়ে নিয়মটি পরীক্ষা করুন
Gauss, ক্যালেন্ডার বা অ্যালগরিদমের ইতিহাসে বানানো সাল ও বইয়ের নামবিশ্বস্ত পাঠ্যবই বা মূল উৎসে মিলিয়ে নিন; নিশ্চিত না হলে সালটি বাদ দিন

AI-এর কাছে modular হিসাব বা প্রমাণ চাইলে এমন কাঠামো দিন, যাতে প্রতিটি ধাপ আলাদা করে যাচাই করা যায়:

নিচের modular arithmetic-এর প্রশ্নটি সমাধান করো: প্রশ্ন: [এখানে প্রশ্ন, যেমন 7^2026 mod 100] ১. প্রথমে modulus আর প্রতিটি সংখ্যা স্পষ্ট লেখো; ঋণাত্মক সংখ্যা থাকলে কোন সংজ্ঞায় ভাগশেষ (0 থেকে m − 1) নিচ্ছ তা বলো। ২. ঘাত থাকলে binary রূপ লেখো আর প্রতিটি বর্গের ভাগশেষ আলাদা লাইনে দাও। ৩. কোথাও ঘাত ছোট করলে বলো কোন সংখ্যা n-এর জন্য a^n ≡ 1 প্রমাণ করেছ; শুধু "ঘাত mod m" লিখবে না। ৪. কোথাও কাটাকাটি বা ভাগ করলে gcd-এর শর্তটি দেখাও। ৫. প্রমাণে প্রতিটি ধাপের পাশে কোন উপপাদ্য ব্যবহার হলো তা লেখো। ৬. শেষে Python-এ pow(a, b, m) দিয়ে যাচাইয়ের এক লাইনের কোড দাও, আর মনে রেখো ছোট উদাহরণে পরীক্ষা প্রমাণ নয়।

AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট

সবচেয়ে সস্তা প্রথম পরীক্ষা: AI যে সংখ্যা দিল, সেটি §৫-এর pow_mod_trace দিয়ে চালান। সারণির একটি ঘরের অমিলই দেখিয়ে দেয় ভুলটি কোন ধাপে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১ভাগশেষের হিসাব তো বহু পুরোনো। তাহলে Gauss-এর ≡ চিহ্ন নতুন কী দিল?

ভাগশেষ নিয়ে প্রশ্ন সত্যিই প্রাচীন। ভারতীয়, চীনা আর গ্রিক গণিতে “কোন সংখ্যাকে 3 দিয়ে ভাগ করলে 2, 5 দিয়ে ভাগ করলে 3 থাকে” ধরনের সমস্যা দেখা যায়, যার একটি বিখ্যাত রূপ পর্ব ২৩-এ Chinese Remainder Theorem হিসেবে আসবে। সপ্তদশ-অষ্টাদশ শতকে Fermat আর Euler ভাগশেষের গভীর উপপাদ্যও প্রমাণ করেছেন। কিন্তু তাঁরা লিখতেন কথায়: “a আর b-কে m দিয়ে ভাগ করলে একই অবশিষ্ট থাকে।” প্রতিটি যুক্তিতে এই বাক্য বারবার লিখতে হতো, আর হিসাবের নিয়মগুলো (যোগ, গুণ, ঘাত) প্রতিবার নতুন করে যুক্তি দিয়ে বসাতে হতো।

Carl Friedrich Gauss ১৮০১ সালে, মাত্র চব্বিশ বছর বয়সে, Disquisitiones Arithmeticae প্রকাশ করেন, আর তার শুরুতেই a ≡ b (mod m) লেখার রীতি চালু করেন। চিহ্নটি ইচ্ছা করেই সমান চিহ্নের মতো দেখতে। বার্তাটি ছিল: এটি এক ধরনের সমতা, আর সমতার মতোই এর সঙ্গে হিসাব করা যায়। উপপাদ্য ২.১ বলে এটি reflexive, symmetric, transitive, ঠিক “=”-এর মতো; উপপাদ্য ৩.১ বলে দুই পাশে একই জিনিস যোগ বা গুণ করা চলে, আবার “=”-এর মতো। এই মিলগুলো একবার প্রমাণ হয়ে গেলে প্রতিটি অঙ্কে আর ভাবতে হয় না; বীজগণিতের পরিচিত অভ্যাস প্রায় পুরোটাই কাজে লাগে, শুধু কাটাকাটির মতো কয়েকটি জায়গায় সাবধান হতে হয়।

ভালো চিহ্ন যে চিন্তাকে এগিয়ে দেয়, এটি তার একটি শ্রেষ্ঠ উদাহরণ। অঙ্ক ৩-এর দুই লাইনের প্রমাণ কথায় লিখলে আধা পাতা হতো। পরে উনিশ আর বিশ শতকে এই ধারণাই বিমূর্ত বীজগণিতে ring, ideal আর quotient-এর ভাষায় বিস্তৃত হয়: ℤm হলো ℤ-কে m-এর গুণিতকগুলোর ideal দিয়ে “ভাগ” করে পাওয়া ring. আজ কম্পিউটার বিজ্ঞানে hashing থেকে cryptography পর্যন্ত সবখানে এই ভাষা ব্যবহার হয়, আর প্রোগ্রামিং ভাষার % অপারেটর তার একটি সরাসরি উত্তরসূরি।

মূল উপলব্ধি: Gauss নতুন তথ্য যতটা না দিয়েছেন, তার চেয়ে বেশি দিয়েছেন একটি ভাষা; ≡ চিহ্ন congruence-কে সমতার মতো ব্যবহারযোগ্য করেছে, আর সেই ভাষাই পরের দুই শতকের সংখ্যাতত্ত্ব আর বীজগণিতের ভিত্তি।

প্র ০২কম্পিউটারের পূর্ণসংখ্যার হিসাব আসলে কোন modulus-এ চলে? Overflow কি একটি ভুল, নাকি modular arithmetic?

একটি 32-bit রেজিস্টারে মাত্র 232 = 4,294,967,296টি আলাদা bit-বিন্যাস সম্ভব। দুটি unsigned 32-bit সংখ্যা যোগ করলে ফল যদি এর বেশি হয়, হার্ডওয়্যার সবচেয়ে উঁচু carry bit ফেলে দেয়। গণিতের ভাষায় ফলটি ঠিক (a + b) mod 232. যেমন 4,000,000,000 + 500,000,000-এর আসল যোগফল 4,500,000,000, কিন্তু রেজিস্টারে থাকে 205,032,704. অর্থাৎ unsigned অঙ্কে কম্পিউটার আসলে ℤ232-এ (বা 64-bit-এ ℤ264-এ) হিসাব করে, আর উপপাদ্য ৩.১-এর কারণে যোগ, বিয়োগ আর গুণ সেখানে সুসংগত। C ভাষার মানদণ্ড unsigned অঙ্কের জন্য ঠিক এই আচরণের নিশ্চয়তা দেয়।

Signed সংখ্যার two's complement রূপও একই গণিত। সেখানে ℤ232-এর প্রতিটি class-এর প্রতিনিধি নেওয়া হয় −231 থেকে 231 − 1-এর মধ্যে, §২-এর complete residue system-এর মতো: যেমন 232 − 1 আর −1 একই class, তাই একই bit-বিন্যাস। এই কারণেই হার্ডওয়্যারে signed আর unsigned যোগের জন্য একটিই adder যথেষ্ট (কম্পিউটার আর্কিটেকচার কোর্সের পর্ব ০৫)। কিন্তু C-তে signed overflow-কে মানদণ্ড undefined behaviour বলে, তাই compiler ধরে নিতে পারে সেটি কখনো ঘটে না, আর অপ্রত্যাশিত কোড বানাতে পারে। এখানেই integer overflow একটি বাস্তব বিপদ: 231 − 1 = 2147483647-এর পরে এক যোগ করলে হিসাব ঘুরে ঋণাত্মক হয়ে যেতে পারে, আর বিল, স্টক বা অ্যারের index-এর হিসাবে তা মারাত্মক ভুল ঘটায়।

Python এই সমস্যা এড়ায় যেকোনো আকারের int দিয়ে, যা প্রয়োজনমতো আরও memory নেয়; তাই এই পর্বের ব্লকে 7222-এর ১৮৮টি অঙ্কও নিখুঁত। দাম হলো গতি: বড় int-এর প্রতিটি অপারেশন যন্ত্রের একটি নির্দেশে হয় না। Hash function আর pseudorandom generator অনেক সময় ইচ্ছা করেই mod 232 বা 264-এর এই স্বাভাবিক ঘোরাকে কাজে লাগায়, কারণ তা প্রায় বিনা খরচে পাওয়া যায়।

মূল উপলব্ধি: নির্দিষ্ট প্রস্থের পূর্ণসংখ্যার হিসাব আসলে ℤ2n-এর হিসাব; সমস্যা দেখা দেয় তখনই, যখন প্রোগ্রামার ভাবেন তিনি সাধারণ ℤ-এ আছেন।

প্র ০৩যেকোনো তারিখ কোন বার, তা হিসাব করার সূত্র কীভাবে কাজ করে? Gregorian ক্যালেন্ডার কত বছরে পুরোপুরি পুনরাবৃত্ত হয়?

একটি সাধারণ বছরে 365 দিন, আর 365 = 52 · 7 + 1, তাই 365 ≡ 1 (mod 7). অর্থাৎ সাধারণ বছর পেরোলে একই তারিখের বার এক ঘর এগোয়; leap year-এ 366 ≡ 2, তাই দুই ঘর। “এ বছর আমার জন্মদিন মঙ্গলবার, আগামী বছর বুধবার” কথাটির পেছনে এই congruence-ই কাজ করে, যদি না মাঝে ২৯ ফেব্রুয়ারি পড়ে।

১৫৮২ সালে চালু হওয়া Gregorian ক্যালেন্ডারে leap year-এর নিয়ম: 4 দিয়ে বিভাজ্য বছর leap, কিন্তু 100 দিয়ে বিভাজ্য হলে নয়, আবার 400 দিয়ে বিভাজ্য হলে leap. তাই 400 বছরে leap year 100 − 4 + 1 = 97টি, আর মোট দিন 400 · 365 + 97 = 146097. আশ্চর্যের বিষয়, 146097 = 7 · 20871, অর্থাৎ 146097 ≡ 0 (mod 7). ফলে Gregorian ক্যালেন্ডার প্রতি 400 বছরে তারিখ আর বার দুটি মিলিয়ে হুবহু পুনরাবৃত্ত হয়: 2026 সালের ক্যালেন্ডার আর 2426 সালের ক্যালেন্ডার এক। এর একটি মজার ফল: 400 বছরের চক্রে মাসের ১৩ তারিখ সাতটি বারে সমান সংখ্যায় পড়ে না, কারণ মোট সংখ্যা (4800) 7 দিয়ে বিভাজ্যই নয়।

যেকোনো তারিখের বার বের করার সূত্রগুলো এই হিসাবকেই গুছিয়ে লেখে: বছরের অবদান (প্রতি বছর +1, leap year-এ আরও +1), মাসের শুরুর অবদান (প্রতিটি মাসের দিনসংখ্যা mod 7), আর তারিখ, সব যোগ করে mod 7. উনিশ শতকের শেষ দিকে Christian Zeller এমন একটি সূত্র প্রকাশ করেন বলে উল্লেখিত, যা আজ Zeller's congruence নামে পরিচিত; আর গণিতবিদ John Conway বিশ শতকের শেষ ভাগে মুখে মুখে হিসাবের জন্য “Doomsday” নামে একটি পদ্ধতি জনপ্রিয় করেন বলে জানা যায়। প্রোগ্রামে অবশ্য সাধারণত লাইব্রেরি ব্যবহার করা উচিত, কারণ ইতিহাসে বিভিন্ন দেশ ভিন্ন সময়ে Gregorian ক্যালেন্ডারে গেছে, আর সময় অঞ্চলের নিয়মও বদলায়।

মূল উপলব্ধি: ক্যালেন্ডারের বার হলো দিনসংখ্যার mod 7; 365 ≡ 1 আর 146097 ≡ 0 (mod 7), এই দুটি তথ্যই ক্যালেন্ডারের পুনরাবৃত্তির পুরো গল্প বলে দেয়।

প্র ০৪Repeated squaring গণিতে নির্ভুল। তবু cryptography-তে এর সরল রূপ কেন বিপজ্জনক হতে পারে?

উপপাদ্য ৫.১-এর পদ্ধতিতে প্রতিটি bit-এ একটি বর্গ হয়, কিন্তু গুণ হয় শুধু সেই bit 1 হলে। গণিতের দিক থেকে এতে কোনো সমস্যা নেই। কিন্তু যদি ঘাত b একটি গোপন key হয় (যেমন RSA-র private exponent), তবে প্রতিটি ধাপে কাজের পরিমাণ গোপন bit-টির ওপর নির্ভর করে। যিনি শুধু সময় মাপতে পারেন, বা যন্ত্রের বিদ্যুৎ খরচের সূক্ষ্ম ওঠানামা দেখতে পারেন, তিনি “বর্গ, বর্গ-গুণ, বর্গ, …” প্যাটার্ন থেকে bit-গুলো আন্দাজ করতে পারেন। একে বলে side-channel attack. Paul Kocher ১৯৯৬ সালে সময় মেপে এমন আক্রমণের ধারণা প্রকাশ করেন বলে ব্যাপকভাবে উল্লেখিত, আর পরে বিদ্যুৎ খরচ মেপে আক্রমণের কাজও আসে।

প্রতিরোধের মূল নীতি: গোপন তথ্যের ওপর নির্ভর করে কাজের ক্রম বা পরিমাণ বদলানো যাবে না (constant-time কোড)। একটি পরিচিত কৌশল Montgomery ladder, যেখানে প্রতিটি bit-এ, 0 হোক বা 1, ঠিক একটি গুণ আর একটি বর্গ হয়, শুধু কোন চলকে ফল যাবে তা বদলায়। আরেকটি কৌশল blinding: গণনার আগে ইনপুটকে একটি এলোমেলো মান দিয়ে বদলে নেওয়া, যাতে মাপা সময় আসল ইনপুটের সঙ্গে সম্পর্কহীন হয়। এগুলো গণিতের ফল একটুও বদলায় না, শুধু বাইরের পর্যবেক্ষকের কাছে যা ফাঁস হয় তা বদলায়।

এখান থেকে একটি বড় শিক্ষা: এই পর্বের pow_mod_trace শেখার জন্য চমৎকার, কিন্তু আসল নিরাপত্তার কাজে নিজে লেখা modular exponentiation ব্যবহার করা উচিত নয়। Textbook RSA (padding ছাড়া, ছোট সংখ্যায়) নিজেই অনিরাপদ, আর নিখুঁত গণিতও বাস্তবায়নের ভুলে ভেঙে পড়ে। বাস্তব ব্যবস্থায় যাচাই করা লাইব্রেরি, যথাযথ padding আর বড় key লাগে; তার ব্যবহারিক দিক সাইবার সিকিউরিটি কোর্সের পর্ব ০৭ আর পর্ব ১০-এ আছে, আর RSA-র গণিত এই কোর্সের পর্ব ২৮ “RSA-এর গণিত — key, encryption আর কেন কাজ করে”-এ আসবে।

মূল উপলব্ধি: একটি algorithm গাণিতিকভাবে সঠিক হলেই নিরাপদ নয়; গোপন তথ্যের ওপর নির্ভর করে কাজের সময় বদলালে সেই সময়ই তথ্য ফাঁস করে।

প্র ০৫“নয় বাদ দেওয়া” পদ্ধতি দিয়ে হিসাব মেলানো কতটা নির্ভরযোগ্য? কোন ভুল এটি ধরতে পারে না?

পুরোনো দোকানের খাতায় বা স্কুলের অঙ্কে একটি কৌশল চলত: 3456 × 789 = 2726784 ঠিক কি না দেখতে দুই পাশের অঙ্ক-যোগফল mod 9 মেলানো। 3456 ≡ 18 ≡ 0, তাই বাঁ পাশ ≡ 0; ডান পাশের অঙ্ক-যোগফল 2 + 7 + 2 + 6 + 7 + 8 + 4 = 36 ≡ 0. মিলে গেল। যুক্তিটি উপপাদ্য ৪.১ আর ৩.১: গুণফলের mod 9 উৎপাদকগুলোর mod 9-এর গুণফল। এই কৌশল মধ্যযুগের ভারতীয় ও আরব গণিতের লেখায় পাওয়া যায় আর Fibonacci-এর Liber Abaci (১২০২)-এর মাধ্যমে ইউরোপে ছড়ায় বলে সাধারণভাবে উল্লেখ করা হয়।

কিন্তু পরীক্ষাটি একমুখী। অমিল হলে হিসাব নিশ্চিতভাবে ভুল; মিল হলে শুধু বলা যায় ভুল থাকলে তা 9-এর গুণিতক পরিমাণের। আর ঠিক এমন ভুলই সবচেয়ে সাধারণ: দুটি পাশাপাশি অঙ্ক উল্টে লেখা (2726784-এর বদলে 2727684) অঙ্ক-যোগফল একই রাখে, তাই ধরা পড়ে না। একটি শূন্য বাদ পড়া বা বাড়তি বসানো, 9-কে 0 লেখা, এগুলোও ধরা পড়ে না। মোটামুটি ভাবে, এলোমেলো একটি ভুল নয় ভাগের এক ভাগ সম্ভাবনায় চোখ এড়িয়ে যায়।

11-এর পরীক্ষা পাশাপাশি অঙ্কের অদলবদল অনেক ক্ষেত্রেই ধরে, কারণ সেখানে অঙ্কগুলোর চিহ্ন আলাদা। এই পর্যবেক্ষণ থেকেই check digit-এর নকশা আসে: অঙ্কগুলোকে আলাদা ওজন (weight) দিয়ে গুণ করে যোগফল mod 10 বা mod 11 নেওয়া, যাতে সবচেয়ে সাধারণ টাইপের ভুল (একটি অঙ্ক ভুল, দুটি পাশাপাশি অঙ্ক উল্টানো) ধরা পড়ে। কোন নকশা ঠিক কোন ভুল ধরে, তা প্রমাণসহ পর্ব ২৬-এ আসবে; সেখানে দেখা যাবে mod 11 (একটি মৌলিক সংখ্যা) কেন mod 10-এর চেয়ে বেশি ভুল ধরে। আরও শক্তিশালী ভুল-শনাক্তকরণ আর সংশোধন আসবে পর্ব ৭২ “Error-correcting code — parity, Hamming code আর Hamming distance”-এ।

মূল উপলব্ধি: modular check শুধু এমন ভুল ধরে, যা ভাগশেষ বদলায়; কোন ভুল ধরা হবে তা ঠিক করে modulus আর ওজনের নকশা, তাই check digit-এর পেছনে আসলে congruence-এর হিসাব।

অনুশীলন

আগে খাতায় করুন, তারপর উত্তর খুলে মেলান।

  1. প্রমাণ: প্রমাণ করুন, প্রতিটি পূর্ণসংখ্যা n-এর জন্য n2 ≡ 0 বা 1 (mod 4). তারপর দেখান, 4k + 3 আকারের কোনো সংখ্যা (যেমন 3, 7, 11, 2027) দুটি পূর্ণবর্গের যোগফল হতে পারে না।
    1. উপপাদ্য ২.৩ অনুযায়ী n ≡ 0, 1, 2 বা 3 (mod 4). Case-ভিত্তিক প্রমাণ (পর্ব ০৯)। অনুসিদ্ধান্ত ৩.২-এ n2 ≡ 02, 12, 22 বা 32, অর্থাৎ 0, 1, 4 ≡ 0, বা 9 ≡ 1. তাই n2 ≡ 0 বা 1. ∎
    2. দ্বিতীয় দাবি, contradiction-এ: ধরি a2 + b2 = 4k + 3. ধাপ ১ অনুযায়ী বাঁ পাশ ≡ 0 + 0, 0 + 1, 1 + 0 বা 1 + 1, অর্থাৎ 0, 1 বা 2 (mod 4). ডান পাশ ≡ 3. বিরোধ। ∎
    3. §৬-এর ব্লক n = 0 থেকে 999-এ n2 mod 4-এর মান হিসেবে শুধু {0, 1} পেয়েছে, প্রমাণের সঙ্গে মেলে।
  2. হিসাব: (ক) repeated squaring দিয়ে 5117 mod 19 বের করুন আর গুণ গুনুন। (খ) 22026-এর শেষ অঙ্ক কত? (গ) আজ শনিবার হলে ১,০০০ দিন পরে কী বার?

    (ক) 117 = 11101012 = 64 + 32 + 16 + 4 + 1, L = 6. বর্গ mod 19: x0 … x6 = 5, 6, 17, 4, 16, 9, 5 (যেমন 52 = 25 ≡ 6, 62 = 36 ≡ 17, 172 = 289 ≡ 4). 1-bit যেখানে: i = 0, 2, 4, 5, 6. ক্রমে গুণ: 5 · 17 = 85 ≡ 9; 9 · 16 = 144 ≡ 11; 11 · 9 = 99 ≡ 4; 4 · 5 = 20 ≡ 1. উত্তর 5117 ≡ 1 (mod 19); ৬টি বর্গ আর ৪টি গুণ, মোট ১০ ≤ 2 · 6. (উত্তর 1 আসার কারণ 59 ≡ 1 (mod 19) আর 9 ∣ 117; এমন চক্র পর্ব ২৪-এর বিষয়।)

    (খ) 2k-এর শেষ অঙ্ক 2, 4, 8, 6 চক্রে ঘোরে (k ≥ 1). 2026 = 4 · 506 + 2, তাই শেষ অঙ্ক চক্রের দ্বিতীয়টি, 4.

    (গ) 1000 = 142 · 7 + 6, তাই 1000 ≡ 6 (mod 7). শনি = 6, আর (6 + 6) mod 7 = 5, শুক্রবার। সব উত্তর scratch-এ pow আর % দিয়ে যাচাই করা।

  3. আউটপুট অনুমান: কোড না চালিয়ে বলুন কী ছাপা হবে। তারপর যেকোনো Python-এ চালিয়ে মেলান।
    print(-17 % 5, -17 // 5)
    print(pow(3, 200, 13), 10**20 % 9)
    print((7 * 8) % 6 == ((7 % 6) * (8 % 6)) % 6)
    print(sum(int(c) for c in "918082") % 9, 918082 % 9)
    3 -4
    9 1
    True
    1 1

    প্রথম লাইন: Python-এর // floor নেয়, −3.4 থেকে −4, আর ভাগশেষ −17 − (−4)(5) = 3. দ্বিতীয় লাইন: 3200 mod 13 = 9 (§৫-এর stepper), আর 10 ≡ 1 (mod 9) বলে 1020 ≡ 1. তৃতীয় লাইন উপপাদ্য ৩.১(গ): 56 mod 6 = 2, আর (1 · 2) mod 6 = 2. চতুর্থ লাইন উপপাদ্য ৪.১: অঙ্ক-যোগফল 28 ≡ 1, আর সংখ্যাটিও ≡ 1 (mod 9).

  4. প্রমাণের ভুল ধরুন: একজন লিখলেন: “দাবি: a ≡ b (mod m) হলে প্রতিটি ধনাত্মক c-তে ca ≡ cb (mod m). প্রমাণ: অনুসিদ্ধান্ত ৩.২ বলে congruent সংখ্যার ঘাত congruent, আর a ≡ b, তাই ca ≡ cb. ∎” ভুল কোথায়, আর একটি counterexample দিন।
    • ভুল: অনুসিদ্ধান্ত ৩.২ বলে ভিত্তি congruent হলে একই ঘাতে ফলও congruent: a ≡ b ⇒ ak ≡ bk. এখানে উল্টো জিনিস ব্যবহার করা হয়েছে: ভিত্তি একই (c), ঘাত দুটি congruent. উপপাদ্যটি এ বিষয়ে কিছুই বলে না।
    • Counterexample: m = 3, a = 1, b = 4 (1 ≡ 4 (mod 3)), c = 2. তখন 21 = 2, কিন্তু 24 = 16 ≡ 1 (mod 3), আর 2 ≢ 1.
    • সঠিক দাবি: cn ≡ 1 (mod m) এমন কোনো n ≥ 1 জানা থাকলে a ≡ b (mod n) ⇒ ca ≡ cb (mod m) (a, b ≥ 0). প্রমাণ: a = b + kn ধরে k ≥ 0 হলে ca = cb(cn)k ≡ cb; k < 0 হলে a আর b-এর ভূমিকা বদলে একই যুক্তি। ∎ এমন n কীভাবে পাওয়া যায়, তা পর্ব ২৪-এর Fermat আর Euler-এর উপপাদ্য বলে।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমRepeated squaring দিয়ে ab mod m বের করার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. ঘাত b-কে binary রূপে লেখা
  2. a mod m থেকে শুরু করে পরপর বর্গ করে প্রতিবার mod m নেওয়া
  3. b-এর যেসব bit 1, সেই অবস্থানের বর্গগুলো বেছে নেওয়া
  4. বাছাই করা মানগুলো গুণ করা, প্রতিটি গুণের পরে mod m নিয়ে
  5. ফলটি অন্য পথে (যেমন Python-এর pow(a, b, m)) মিলিয়ে নেওয়া

নিচের কোন congruence সত্য?

  • −17 ≡ 3 (mod 5)
  • 17 ≡ 3 (mod 5)
  • −17 ≡ −3 (mod 5)
  • 12 ≡ 5 (mod 6)

2100-কে 7 দিয়ে ভাগ করলে ভাগশেষ কত?

  • 1
  • 2
  • 4
  • 0

Python-এ print(-7 % 3, -7 // 3) কী ছাপে?

  • -1 -2
  • 1 -2
  • 2 -3
  • -1 -3

নিচের কোন সংখ্যাটি 11 দিয়ে বিভাজ্য?

  • 91961
  • 19916
  • 99161
  • 91916

ℤ6-এর গুণ (a ·6 b = ab mod 6) সম্পর্কে কোনটি সত্য?

  • প্রতিটি অশূন্য উপাদানকে কিছু দিয়ে গুণ করলে 1 পাওয়া যায়
  • দুটি অশূন্য উপাদানের গুণফল 0 হতে পারে
  • ac = bc আর c ≠ 0 হলে সবসময় a = b
  • a ·6 b আর b ·6 a সাধারণত আলাদা

b = 2k (k ≥ 1) হলে repeated squaring দিয়ে ab mod m বের করতে মোট কয়টি গুণ লাগে?

  • ঠিক k-টি
  • ঠিক 2k-টি
  • 2k − 1টি
  • m-এর ওপর নির্ভর করে

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. সংজ্ঞা: a ≡ b (mod m) ⇔ m ∣ (a − b) ⇔ a = b + km ⇔ একই ভাগশেষ। “≡” একটি সম্পর্ক, “a mod m” একটি সংখ্যা।
  2. ℤm: ≡ একটি equivalence relation; ঠিক m-টি congruence class, প্রতিনিধি 0, …, m − 1. ঋণাত্মক সংখ্যায় Python-এর % গণিতের সঙ্গে মেলে, C-র % মেলে না।
  3. হিসাবের নিয়ম: যোগ, বিয়োগ, গুণ আর একই ঘাত congruence রক্ষা করে; কাটাকাটি শুধু gcd(c, m) = 1 হলে; ঘাতকে mod m করা নিষেধ।
  4. বিভাজ্যতা: 10 ≡ 1 (mod 9) থেকে অঙ্ক-যোগফলের নিয়ম, 10 ≡ −1 (mod 11) থেকে একান্তর যোগফলের নিয়ম, 1000 ≡ −1 থেকে 7 আর 13-এর তিন অঙ্কের দলের নিয়ম।
  5. Repeated squaring: ab mod m বড়জোর 2⌊log2 b⌋টি গুণে; Python-এ pow(a, b, m). শেষ অঙ্ক আর সপ্তাহের দিন mod 10, 100 আর 7-এর অঙ্ক।

পরের পর্বে: পর্ব ২৩ “Linear congruence আর Chinese Remainder Theorem”: কখন mod m-এ ভাগ করা যায়, ax ≡ b-এর সমাধান কয়টি, আর একাধিক ডেলিভারি-চক্র একসঙ্গে মেলানোর পদ্ধতি।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান