026 — Remainder Basics¶
এই note দিয়ে তুমি Level 2-এর দরজায় পা রাখছ। Level 0-তে
%ছিল last digit বের করার কাঁচি, Level 1-এ divisibility-র পরীক্ষক। এখন একই%-কে নতুন চোখে দেখব — একটা ঘড়ি, যেখানে সংখ্যা ঘুরে ঘুরে ছোট পরিসরে নেমে আসে। ধীরে পড়ো, ঘড়ির ছবিটা আগে মাথায় বসাও।
Source¶
- Original source: Classic exercise (modular arithmetic-এর একদম গোড়ার অনুশীলন)
- Platform: Classic exercise — কোনো একক judge নেই; প্রায় সব CP intro-তে থাকে
- Topic: Math-based programming fundamentals → Level 2: Modular Arithmetic
- Difficulty: Easy
- Pattern: mod basics (remainder + congruence)
- Basic idea inherited from: 001 — Even or Odd (
%operator-এর সাথে প্রথম পরিচয়)
1. Problem in simple words¶
দুটো integer a আর m দেওয়া আছে (m > 0)। বলতে হবে a-কে m দিয়ে ভাগ করলে কত remainder (বাকি) থাকে — মানে a % m।
সঙ্গে আরও দুটো ছোট প্রশ্ন একই গোত্রের:
- দুটো সংখ্যা
aআরbকি একই remainder দেয়mদিয়ে? (একে বলে congruent mod m) aযদি negative হয়, remainder কত হওয়া উচিত — আমরা চাই সবসময় 0 থেকে m−1 এর মধ্যে একটা non-negative উত্তর।
দেখতে তুচ্ছ, কিন্তু এই remainder-ই পুরো Level 2-এর ভিত। ঠিকঠাক বুঝলে যোগ, গুণ, power, এমনকি ভাগ — সব দাঁড়িয়ে যাবে।
2. What is the math idea?¶
মূল idea হলো division with remainder আর তার থেকে আসা modular arithmetic।
যেকোনো integer a আর positive m-এর জন্য ঠিক একভাবে লেখা যায়:
a = q × m + r, যেখানে 0 ≤ r < m
এখানে q হলো quotient (ভাগফল), r হলো remainder (বাকি)। সেই r-ই a % m।
আর congruence: a ≡ b (mod m) মানে a আর b-কে m দিয়ে ভাগ করলে remainder একই — অর্থাৎ (a − b) পুরোপুরি m দিয়ে ভাগ যায়। ঘড়ির ভাষায়: দুজন একই ঘরে দাঁড়িয়ে।
3. Which basic idea does this inherit from?¶
এটা সরাসরি 001 — Even or Odd থেকে এসেছে। সেখানে তুমি n % 2 দিয়ে শিখেছিলে remainder বলে দেয় "কী বাকি রইল"। % 2 ছিল মাত্র দুই ঘরের ঘড়ি (0 আর 1)।
এখন সেই একই idea-কে generalize করছি: 2-এর জায়গায় যেকোনো m। % 2 → % m। দুই ঘরের ঘড়ি → m ঘরের ঘড়ি। even/odd-এর parity ছিল mod 2-এর special case; remainder basics হলো তার পূর্ণ রূপ।
মানে আজ তুমি নতুন কিছু শিখছ না, বরং চেনা %-কে আরও বড় canvas-এ দেখছ।
4. Real-life analogy¶
ভাবো একটা 12 ঘণ্টার ঘড়ি। এখন 9টা বাজে। 5 ঘণ্টা পরে কয়টা? অঙ্কে 9 + 5 = 14, কিন্তু ঘড়ি বলে 2টা — কারণ কাঁটা 12 পেরিয়ে আবার শুরু থেকে গোনে।
এই "12 পেরোলেই আবার 0 থেকে" — এটাই % 12। 14 % 12 = 2।
remainder মানে: পুরো চক্কর (12, 24, 36...) বাদ দিয়ে কাঁটা শেষ পর্যন্ত কোন ঘরে দাঁড়াল। আর negative? ঘড়িতে পেছনে হাঁটা — 12টা থেকে 3 ঘণ্টা পিছালে 9টা, তাই −3 ≡ 9 (mod 12)।
5. Visual explanation¶
প্রথমে number line-কে ভাঁজ করে ঘড়ি বানানো — m = 5 ধরে:
mod 5 এর ঘড়ি (5টা ঘর: 0,1,2,3,4):
number line: 0 1 2 3 4 | 5 6 7 8 9 | 10 11 12 13 14
remainder: 0 1 2 3 4 | 0 1 2 3 4 | 0 1 2 3 4
^একটা চক্কর^ ^আবার শুরু^ ^আবার শুরু^
7 % 5 = 2, 12 % 5 = 2, 2 % 5 = 2 -> 7, 12, 2 ঘড়ির একই ঘরে (congruent)
এবার negative-এর ছবি — পেছনে হাঁটা:
mod 5 এ পেছনে হাঁটা:
... -3 -2 -1 | 0 1 2 3 4
... 2 3 4 | 0 1 2 3 4
^
-3 % 5 = 2 (5 ঘরের ঘড়িতে 3 ঘর পিছালে 2-তে পৌঁছাই)
খেয়াল করো — উত্তর সবসময় 0 থেকে 4-এর মধ্যে; ঘড়ি কখনো ঘরের বাইরে যায় না।
6. Example input and output¶
a m -> a % m ব্যাখ্যা
--------------------------------------------------
17 5 -> 2 17 = 5×3 + 2
20 4 -> 0 20 = 4×5 + 0 (নিঃশেষে ভাগ)
3 7 -> 3 a < m, পুরোটাই remainder
-3 5 -> 2 Python: পেছনে 3 ঘর -> 2
-7 3 -> 2 -7 = 3×(-3) + 2
0 9 -> 0 0 যেকোনো কিছু দিয়ে ভাগে 0
congruence-এর উদাহরণ:
17 ≡ 2 (mod 5)? 17 % 5 = 2, 2 % 5 = 2 -> হ্যাঁ, congruent
17 ≡ 3 (mod 5)? 17 % 5 = 2, 3 % 5 = 3 -> না
দুটো জিনিস মনে রাখো: a < m হলে remainder পুরো a-ই; আর Python-এ negative % সবসময় non-negative দেয় (এটা একটা আশীর্বাদ, পরে দেখবে)।
7. Brute force thinking¶
% operator না জানলে remainder কীভাবে বের করতে? "হাতে গুনে" — a থেকে বারবার m বিয়োগ করতে থাকো যতক্ষণ না 0 থেকে m−1-এর মধ্যে কিছু থাকে:
def remainder_brute(a, m):
if a >= 0:
while a >= m:
a -= m # একটা চক্কর সরিয়ে রাখলাম
return a
else:
while a < 0:
a += m # negative হলে উল্টো দিকে, non-negative করতে
return a
এটা আসলে ঘড়ির কাঁটা এক এক ঘর করে ঘোরানোর মতো — যতক্ষণ না ঠিক ঘরে পৌঁছাই। ঠিক উত্তরই দেয়।
8. Why brute force may be slow¶
সমস্যা: loop ঘোরে প্রায় a / m বার। a = 1,000,000,000 আর m = 3 হলে loop প্রায় 33 কোটি বার চলবে — interview-তে এটা সরাসরি Time Limit Exceeded।
a = 1000000000, m = 3 হলে:
brute force: ~333000000 বার বিয়োগ (ধীর, O(a/m))
smart way : ঠিক 1 বার % (চোখের নিমেষে, O(1))
মূল শিক্ষা: যেটা division-এর একটা operation-এ জানা যায়, তার জন্য বিয়োগের loop চালানো অপচয়। আমাদের কাছে সেই এক-ধাপের tool আছে — %।
9. Better thinking¶
সরাসরি জিজ্ঞেস করি: "m দিয়ে ভাগ করলে কত বাকি?" সেটাই a % m। একটাই operation, a যত বড়ই হোক।
আর "একই ঘরে কিনা" (congruent) জানতে দুজনের remainder মেলালেই হলো:
এটাই O(1) — constant time। brute force-এর কোটি কোটি বিয়োগ এক ধাপে নেমে এল।
10. Thinking tweak¶
আসল "আহা!" মুহূর্ত: remainder মানে চক্কর ফেলে দিয়ে শুধু "শেষ অবস্থান"। পুরো a কত বড় তাতে কিছু আসে যায় না — m-এর কতগুলো পূর্ণ চক্কর হলো সেটা ভুলে যাও, শুধু ঘড়ির কাঁটা কোথায় থামল সেটাই উত্তর।
আর negative-এর tweak: যদি কখনো ভাষা-নিরপেক্ষ safe উত্তর চাও (C++-এ % negative দিতে পারে), এই রূপটা মনে রাখো —
((a % m) + m) % m
এটা সবসময় 0 থেকে m−1-এর মধ্যে non-negative দেয়। Python-এ এমনিতেই ঠিক, কিন্তু এই অভ্যাসটা পরের সব problem-এ বাঁচাবে।
11. Step-by-step dry run¶
চলো a = 17, m = 5 ধীরে চালাই — আগে brute force (চক্কর সরানো):
| step | a (শুরুতে) | a >= 5? | a -= 5 | হাতে রইল |
|---|---|---|---|---|
| 1 | 17 | হ্যাঁ | 17 - 5 = 12 | 12 |
| 2 | 12 | হ্যাঁ | 12 - 5 = 7 | 7 |
| 3 | 7 | হ্যাঁ | 7 - 5 = 2 | 2 |
| 4 | 2 | না (loop থামল) | — | 2 |
তিনটে পূর্ণ চক্কর (5×3 = 15) সরিয়ে হাতে রইল 2।
এবার smart way — এক লাইনে:
দুটোই বলছে 2। একই সত্য, কিন্তু % সোজা উত্তরে নিয়ে গেল।
এবার negative a = -3, m = 5 — Python কীভাবে দেখে:
12. Python solution¶
def remainder(a, m):
"""a-কে m দিয়ে ভাগ করলে remainder (Python: সবসময় non-negative যদি m > 0)।"""
return a % m
def remainder_safe(a, m):
"""ভাষা-নিরপেক্ষ non-negative remainder (C++-এও কাজ করবে এমন রূপ)।"""
return ((a % m) + m) % m
def congruent(a, b, m):
"""a আর b কি একই remainder দেয় m দিয়ে? (a ≡ b mod m)"""
return a % m == b % m
# --- ছোট test গুলো (সব pass করা উচিত) ---
assert remainder(17, 5) == 2
assert remainder(20, 4) == 0
assert remainder(3, 7) == 3
assert remainder(-3, 5) == 2 # Python: non-negative
assert remainder(0, 9) == 0
assert remainder_safe(-3, 5) == 2
assert remainder_safe(-7, 3) == 2
assert remainder_safe(17, 5) == 2 # positive-এও একই উত্তর
assert congruent(17, 2, 5) is True # 17 ≡ 2 (mod 5)
assert congruent(17, 3, 5) is False
assert congruent(-3, 2, 5) is True # -3 আর 2 একই ঘরে
print(remainder(17, 5)) # 2
print(remainder_safe(-3, 5)) # 2
print(congruent(17, 2, 5)) # True
print("সব test pass!")
13. Line-by-line explanation¶
এটাই হৃদয়। a % m সরাসরি Python-এর modulo — m > 0 হলে উত্তর 0 থেকে m−1-এর মধ্যে, এমনকি a negative হলেও non-negative।
ভাষা-নিরপেক্ষ রূপ। ভেতরের a % m-এ যদি কোনো ভাষায় negative আসত, + m করে আবার % m নিলে সেটা 0 থেকে m−1-এ চলে আসে। Python-এ এমনিতেই ঠিক, তাই এটা একই উত্তর — কিন্তু অভ্যাস গড়ার জন্য রাখা।
দুজনের remainder সমান কিনা মেলায়। সমান মানে একই ঘরে — congruent। == একটা True/False expression, তাই সরাসরি return।
বাকি assert লাইনগুলো নিজেদের চেক করছে — উত্তর আশানুরূপ না হলে program সেখানেই থেমে error দেবে। সব ঠিক থাকলে শেষে সব test pass! ছাপে।
14. Output walkthrough¶
চালালে যা ছাপবে:
প্রথম লাইন remainder(17, 5) থেকে → 2। দ্বিতীয় লাইন remainder_safe(-3, 5) থেকে → 2 (negative হলেও non-negative)। তৃতীয় লাইন congruent(17, 2, 5) → True (দুজনেই remainder 2)। তার আগের সব assert চুপচাপ pass করেছে। সবশেষে সব test pass!।
15. Time complexity¶
O(1) — একটাই % operation, a বা m যত বড়ই হোক। সংখ্যার মানের সাথে কাজ বাড়ে না।
(সূক্ষ্ম কথা: অতি-বিশাল সংখ্যায় modulo-র খরচ digit সংখ্যার সাথে সামান্য বাড়ে, কিন্তু সাধারণ interview-র মাপে একে নিশ্চিন্তে O(1) ধরি — brute force-এর O(a/m)-এর তুলনায় আকাশ-পাতাল পার্থক্য।)
16. Space complexity¶
O(1) — কোনো list, array লাগছে না; শুধু গুটিকয় variable। input ছাড়া আলাদা জায়গা প্রায় শূন্য।
17. Common mistakes¶
- negative remainder নিয়ে ভাষার পার্থক্য ভুলে যাওয়া — Python-এ
-3 % 5 == 2, কিন্তু C++/Java-তে-3 % 5 == -3। ভাষা-নিরপেক্ষ safe রূপ((a % m) + m) % mমনে রাখো। m = 0ভুলে যাওয়া — 0 দিয়ে ভাগ অসংজ্ঞায়িত; PythonZeroDivisionErrorদেবে। সবসময় ধরে নাওm > 0।- congruence-কে সমতা ভাবা —
17 ≡ 2 (mod 5)মানে 17 আর 2 সমান নয়; শুধু একই ঘরে।==নয়, remainder মেলানো। a < mহলে remainder 0 ভাবা — না, তখন remainder পুরোa-ই (যেমন3 % 7 = 3)।m = 1ভুলে যাওয়া — যেকোনো সংখ্যা mod 1 = 0 (এক ঘরের ঘড়ি)। পরের problem-এ এটা edge case হয়ে ফিরবে।
18. Harder problems that inherit this idea¶
- CSES — Exponentiation (https://cses.fi/problemset/task/1095) — এই remainder-এর উপরেই fast power দাঁড়ায়; প্রতি step-এ
% mনেওয়াটা এখানকার সরাসরি বিস্তার। - 031 (Large Number Mod) — বিশাল সংখ্যার remainder digit-by-digit; এই basic remainder + Level 0-এর digit loop একসাথে।
- 037 (Clock Arithmetic Problems) — এই note-এর ঘড়ি analogy-টাই পুরো একটা problem হয়ে ফিরবে।
19. Pattern learned¶
Remainder + congruence as a clock — a % m মানে চক্কর ফেলে দিয়ে ঘড়ির শেষ অবস্থান (0 থেকে m−1)। আর a ≡ b (mod m) মানে দুজন একই ঘরে। negative-এ safe রূপ ((a % m) + m) % m। এই ঘড়ির ছবিটাই পুরো Level 2-এর ভিত।
20. Final summary¶
ভবিষ্যতের আমাকে এক লাইনে: "remainder = m-ঘরের ঘড়িতে কাঁটার শেষ অবস্থান; a % m দিয়ে এক ধাপে পাই; negative হলে ((a%m)+m)%m safe; congruent মানে একই ঘর। % 2 ছিল এর শিশু-রূপ।"
পরের ধাপ → 027 — Modular Addition (এই ঘড়িতে যোগ করলে কী হয়)। সম্পর্কিত → 037 — Clock Arithmetic Problems, 031 — Large Number Mod।
ফিরে দেখা: এই 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" এমন দাবি করা হয়নি — বরং "common interview pattern" / "CP-তে নিত্য দরকারি" লেখা।