কনটেন্টে যান

004 — Reverse Number

002-003 জুড়ে আমরা digit ভাঙা শিখেছি — % 10 দিয়ে তুলে, // 10 দিয়ে ফেলে। আজ প্রথমবার উল্টোটা শিখব: digit জুড়ে নতুন একটা সংখ্যা বানানো। মূল মন্ত্র একটাই লাইন — rev = rev * 10 + d। এই এক লাইন একবার বুঝে গেলে 005 (palindrome) প্রায় বিনা পরিশ্রমে হাতে চলে আসবে। ধীরে যাও, dry run-টা নিজে হাতে করো।

Problem link ও উৎস

  • Original source: Classic beginner exercise (related practice: LeetCode — Reverse Integer — https://leetcode.com/problems/reverse-integer/)
  • Platform: Classic exercise / LeetCode (related)
  • Topic: Math-based programming fundamentals → Level 0: Absolute Basics
  • Difficulty: Easy
  • Pattern: number building (rev * 10 + d)
  • Basic idea inherited from: 002 (Sum of Digits)

প্রশ্ন ও উদাহরণ

সহজ করে প্রশ্ন

একটা integer n দেওয়া আছে। তার digit গুলোকে উল্টো করে নতুন একটা সংখ্যা বানাতে হবে।

যেমন 1234 উল্টালে হয় 4321। 120 উল্টালে হয় 21 (সামনের 0 আপনাআপনি মিলিয়ে যায় — 021 মানে তো 21)।

আমরা str ছাড়া, শুধু arithmetic (%, //, আর একটা গুণ-যোগ) দিয়ে এটা করব — কারণ এই "সংখ্যা বানানো" কৌশলটা পরের অনেক problem-এ ঘুরেফিরে লাগবে।

Input ও output

উদাহরণ
input   ->  output   (কীভাবে)
------------------------------
 1234   ->   4321
    7   ->      7    (একটাই digit, উল্টালেও একই)
    0   ->      0    (digit একটাই: 0)
  120   ->     21    <-- সাবধান! সামনের 0 মিলিয়ে যায় (021 = 21)
 1000   ->      1    (0001 = 1)
-1234   ->  -4321    (চিহ্ন রেখে digit উল্টানো)

ওই 120 -> 21 লাইনটা মনে রাখো — reverse করলে trailing zero গুলো leading zero হয়ে যায়, আর সংখ্যায় leading zero থাকে না, তাই তারা মিলিয়ে যায়। এটা bug নয়, গণিতের স্বাভাবিক আচরণ।

চেনা উদাহরণে বোঝা

ভাবো তুমি একটা খালি শেলফে বই সাজাচ্ছ, কিন্তু নিয়ম হলো — প্রতিটা নতুন বই সবসময় বাঁ-প্রান্তে রাখতে হবে।

  • নতুন বই এলে, শেলফে আগে থেকে থাকা সব বই এক ঘর ডানে সরিয়ে বাঁয়ে জায়গা করো → এটাই rev * 10 (সবাইকে সরানো)
  • তারপর নতুন বইটা বাঁ-প্রান্তে বসাও → এটাই + d

কৌটো (n) থেকে বই বেরোচ্ছে ডান প্রান্ত থেকে (4, তারপর 3, তারপর 2, 1), কিন্তু শেলফে (rev) সবসময় বাঁয়ে বসছে। তাই যে বই আগে বেরিয়েছে (4) সে শেলফের সবচেয়ে বাঁয়ে — মানে সংখ্যাটা উল্টে গেল! 4321।

ভিত্তির ধারণা

আগের কোন ধারণাটি লাগবে

সরাসরি 002 (Sum of Digits) থেকে — peeling loop (while n > 0: ... n //= 10) হুবহু এক।

002 আর 004-এর ভেতরের লাইন পাশাপাশি রাখো:

উদাহরণ
002 (sum):      total = total + (n % 10)       # শুধু মান জমাও
004 (reverse):  rev   = rev * 10 + (n % 10)    # মান জমাও, কিন্তু আগে এক ঘর বামে ঠেলে
                        ^^^^^^^^
                   নতুন অংশ — জায়গা বানানো

দেখো — 002-এ আমরা শুধু digit-এর মান যোগ করেছিলাম (ক্রম গুরুত্বহীন ছিল)। 004-এ ক্রম খুব গুরুত্বপূর্ণ, তাই প্রতিটা পুরোনো digit-কে বামে ঠেলে জায়গা করে দিই। peeling এক, কিন্তু "প্রতি ধাপে কী করব" বদলে গেল — এটাই inheritance: পুরোনো loop + একটা নতুন মোচড়।

ভেতরের অঙ্ক

002-এর digit-কাটার কাঁচি (% 10 আর // 10) এখানেও আছে — কিন্তু সাথে একটা নতুন জিনিস: জায়গা বানানো।

মূল চিন্তা: ডান দিক থেকে digit তুলছি, আর প্রতিটা নতুন digit-কে বসানোর আগে আগের সব digit-কে এক ঘর বামে ঠেলে দিচ্ছি (× 10), তারপর নতুন digit-টা একদম ডানে বসাচ্ছি (+ d):

উদাহরণ
rev = 0
যতক্ষণ n > 0:
    d = n % 10           # last digit তুলে নাও
    rev = rev * 10 + d   # আগের সব এক ঘর বামে ঠেলো, d-কে ডানে বসাও
    n //= 10             # সেই digit ফেলে দাও

rev * 10 হলো place value-র জাদু — একটা সংখ্যাকে 10 দিয়ে গুণ মানে প্রতিটা digit এক ঘর বামে সরে গিয়ে ডানে একটা খালি (0) ঘর তৈরি হয়। সেই খালি ঘরেই নতুন digit বসে।

ধাপে ধাপে সমাধান

প্রথম সরল উপায়

প্রথমে যেটা মাথায় আসে — সংখ্যাটাকে string বানিয়ে Python-এর slicing দিয়ে উল্টে দিই, তারপর আবার int করি:

Python
def reverse_number_str(n):
    sign = -1 if n < 0 else 1
    rev_str = str(abs(n))[::-1]   # "1234" -> "4321"
    return sign * int(rev_str)

[::-1] হলো Python-এর "পুরো জিনিসটা উল্টে দাও" slicing। এটা কাজ করে, ঠিক উত্তর দেয়, পড়তেও সহজ। int() করার সময় leading zero এমনিই মিলিয়ে যায় (int("021") == 21)। তাহলে সমস্যা কোথায়?

কোথায় বেশি কাজ হতে পারে

Time-এর দিক থেকে string version "ধীর" নয় — দুটোই digit সংখ্যার সমান কাজ করে। কিন্তু কিছু লুকোনো খরচ আর সীমাবদ্ধতা আছে:

  • বাড়তি জায়গা: str(n) আর উল্টানো string — দুটো নতুন string বানায়, মানে O(digit) extra space। arithmetic loop-এ সেটা লাগে না (O(1) space)।
  • ভাষা-নির্ভরতা: Python-এ [::-1] সহজ, কিন্তু C++/Java-তে string উল্টে আবার সংখ্যায় ফেরানো ঝামেলার। arithmetic পদ্ধতি যেকোনো ভাষায় একইভাবে চলে — interview-তে এটা বড় সুবিধা।
  • overflow check করা যায় না: LeetCode Reverse Integer-এর মতো problem উল্টানো সংখ্যা 32-bit সীমা ছাড়ালে 0 চায়। string পদ্ধতিতে আগে থেকে সেটা ধরা কঠিন; arithmetic পদ্ধতিতে প্রতি ধাপে check বসানো সহজ।

মূল কথা: string version দিয়ে উত্তর পাওয়া যায়, কিন্তু এই level-এর আসল লক্ষ্য হলো rev * 10 + d কৌশলটা আয়ত্ত করা — কারণ 005 (palindrome)-এ string ছাড়াই reverse লাগবে।

আরও ভালোভাবে ভাবা

String বাদ — সরাসরি সংখ্যার উপরেই "ভাঙো আর বানাও" loop চালাই:

Python
def reverse_number(n):
    sign = -1 if n < 0 else 1
    n = abs(n)
    rev = 0
    while n > 0:
        d = n % 10
        rev = rev * 10 + d   # বামে ঠেলো, নতুন digit বসাও
        n //= 10
    return sign * rev

কোনো নতুন string নেই — শুধু সংখ্যা আর তিনটে operation। জায়গা লাগছে O(1)। ডান থেকে digit তুলে বাঁয়ে জোড়ার ফলেই সংখ্যাটা উল্টে যায়।

ভাবনার পরিবর্তন

আসল "আহা!" মুহূর্ত এক বাক্যে: সংখ্যাকে 10 দিয়ে গুণ মানে সব digit এক ঘর বামে ঠেলে ডানে একটা খালি ঘর বানানো — সেই খালি ঘরেই নতুন digit বসে।

  • ভাঙার সময় (002): % 10 দিয়ে ডান প্রান্ত পড়ি, // 10 দিয়ে ডান প্রান্ত সরাই → সংখ্যা ছোট হয়।
  • বানানোর সময় (004): × 10 দিয়ে বাঁয়ে ঠেলি, + d দিয়ে ডানে বসাই → সংখ্যা বড় হয়।

দুটো একে অন্যের আয়না। আর যেহেতু আমরা ডান থেকে digit তুলে বাঁয়ে বসাচ্ছি, ক্রম উল্টে যায় — এটাই reverse। এই rev * 10 + d লাইন পরে অনেক জায়গায় ফিরবে (palindrome, এমনকি বড় হয়ে "string to integer" parsing-এও)।

ছবিতে বোঝা

1234 থেকে এক-একটা digit বেরিয়ে rev-এ জুড়ছে — frame by frame দেখো কীভাবে rev "বামে ঠেলে, ডানে বসাও" নিয়মে বড় হয়:

উদাহরণ
শুরুতে: n = 1234, rev = 0

frame 1:  d = 4   rev = 0×10 + 4   = 4        n -> 123
frame 2:  d = 3   rev = 4×10 + 3   = 43       n -> 12
frame 3:  d = 2   rev = 43×10 + 2  = 432      n -> 1
frame 4:  d = 1   rev = 432×10 + 1 = 4321     n -> 0   (থামো!)

ফলাফল: rev = 4321

rev * 10 কী করছে সেটা আলাদা করে দেখো — পুরোনো digit গুলো বামে সরে, ডানে একটা 0 ঘর তৈরি হয়, সেখানেই নতুন d বসে:

উদাহরণ
rev = 43   ->   43 × 10 = 430   ->   430 + 2 = 432
              [4][3][_]              [4][3][2]
               ^বামে ঠেলা^           ^d ডানে বসল^

ছোট input-এ ধাপে ধাপে

n = 1234। প্রতি ধাপে চারটে জিনিস লক্ষ করো — শুরুর n, তোলা digit, নতুন rev, আর খোসা-ছাড়ানো n:

step n (শুরুতে) d = n % 10 rev = rev*10 + d n //= 10 (পরে)
1 1234 4 0×10 + 4 = 4 123
2 123 3 4×10 + 3 = 43 12
3 12 2 43×10 + 2 = 432 1
4 1 1 432×10 + 1 = 4321 0

ধাপ 4-এর শেষে n = 0, তাই while n > 0 আর সত্যি নয় — loop থামল। rev = 4321 — ঠিক উল্টো সংখ্যা। নিজে খাতায় এই টেবিলটা আরেকবার বানাও 120 দিয়ে, দেখবে rev = 21 (সামনের 0 মিলিয়ে গেছে)।

কোড ও ব্যাখ্যা

Python

Python
def reverse_number(n):
    """n-এর digit উল্টে নতুন সংখ্যা (arithmetic পদ্ধতি, চিহ্ন রেখে)।"""
    sign = -1 if n < 0 else 1
    n = abs(n)
    rev = 0
    while n > 0:
        d = n % 10           # last digit
        rev = rev * 10 + d   # বামে ঠেলে ডানে বসাও
        n //= 10             # সেই digit ফেলো
    return sign * rev


def reverse_number_str(n):
    """একই উত্তর, string পদ্ধতি (তুলনার জন্য)।"""
    sign = -1 if n < 0 else 1
    return sign * int(str(abs(n))[::-1])


# --- ছোট test গুলো (সব pass করা উচিত) ---
assert reverse_number(1234) == 4321
assert reverse_number(7) == 7
assert reverse_number(0) == 0          # 0 উল্টালে 0
assert reverse_number(120) == 21       # trailing 0 মিলিয়ে যায়
assert reverse_number(1000) == 1
assert reverse_number(-1234) == -4321  # চিহ্ন রেখে উল্টানো
assert reverse_number_str(1234) == 4321    # দুই পদ্ধতি মেলে
assert reverse_number_str(-120) == -21

print(reverse_number(1234))   # 4321
print(reverse_number(120))    # 21
print(reverse_number(-1234))  # -4321
print("সব test pass!")

কোডের প্রতিটি অংশ

উদাহরণ
    sign = -1 if n < 0 else 1

চিহ্নটা আগে আলাদা করে রেখে দিই। negative হলে -1, নাহলে 1। শেষে এই sign দিয়ে গুণ করব, যাতে -1234 উল্টে -4321 হয় (digit গুলো উল্টায়, minus নয়)।

উদাহরণ
    n = abs(n)

এবার চিহ্ন সরিয়ে শুধু digit নিয়ে কাজ করি, যাতে while n > 0 ঠিকমতো চলে।

উদাহরণ
    rev = 0

বানানো সংখ্যা রাখার পাত্র, শুরুতে খালি (0)।

উদাহরণ
    while n > 0:
        d = n % 10
        rev = rev * 10 + d
        n //= 10

চেনা peeling loop, কিন্তু মাঝের লাইনটাই নতুন। n % 10 last digit তোলে; rev * 10 + d আগের rev-কে এক ঘর বামে ঠেলে নতুন digit ডানে বসায়; n //= 10 সেই digit ফেলে দেয়। n //= 10 না থাকলে n কখনো 0 হবে না → infinite loop।

উদাহরণ
    return sign * rev

শেষে আসল চিহ্নটা ফিরিয়ে দিই।

reverse_number_str হলো shortcut — str(abs(n))[::-1] সংখ্যাকে string বানিয়ে উল্টে দেয়, int() আবার সংখ্যায় ফেরায় (leading zero আপনাআপনি মেলে), তারপর sign বসায়।

Output মিলিয়ে দেখা

চালালে ছাপবে:

উদাহরণ
4321
21
-4321
সব test pass!

তিনটা সংখ্যা তিনটা print থেকে: reverse_number(1234) → 4321, reverse_number(120) → 21 (trailing 0 মিলিয়ে গেছে!), reverse_number(-1234) → -4321 (চিহ্ন ঠিক আছে)। তার আগে সব assert চুপচাপ pass করেছে (একটাও fail করলে সেখানেই থেমে error দিত)। শেষ লাইন সব test pass! মানে দুই পদ্ধতির উত্তর মিলেছে, সব ঠিক।

খরচ ও সাধারণ ভুল

সময়

O(number of digits) = O(log₁₀ n)। loop প্রতিবার n-কে 10 দিয়ে ভাগ করে, তাই যতগুলো digit ততবারই ঘোরে। 9-digit সংখ্যা হলে মাত্র 9 বার — সংখ্যা যত বড় হোক, digit সংখ্যা তার লগারিদমের সমান, তাই খুবই দ্রুত।

(কেন log? কারণ একটা সংখ্যার digit সংখ্যা ≈ log₁₀(n) + 1। দশগুণ বড় হলে digit বাড়ে মোটে একটা।)

অতিরিক্ত জায়গা

O(1) — arithmetic version-এ শুধু sign, rev, d, n — গুটিকয় variable, সংখ্যার আকারের সাথে বাড়ে না। (string version-এ O(digit) — কারণ পুরো string দুবার মেমরিতে বানায়।)

যে ভুলগুলো এড়াতে হবে

  1. rev = rev * 10 + d ভুলে শুধু rev += d লেখা — তাহলে digit গুলো জায়গা না পেয়ে যোগ হয়ে যাবে (1234 → 10, sum of digits!)। বামে ঠেলার * 10-টাই reverse-এর প্রাণ।
  2. n //= 10 ভুলে যাওয়া — n একই থেকে যায়, while n > 0 কখনো শেষ হয় না → infinite loop।
  3. negative-এ চিহ্ন হারানো বা ভুল করা — আগে sign আলাদা রেখে abs(n) নিলে নিরাপদ; নাহলে Python-এ -1234 % 10 হয় 6 (অপ্রত্যাশিত), সব গুলিয়ে যায়।
  4. trailing zero নিয়ে দুশ্চিন্তা — 120 -> 21 ঠিকই; leading zero গণিতে মিলিয়ে যায়, এটা কোনো bug নয়।
  5. LeetCode-এ 32-bit overflow ভুলে যাওয়া — Python-এ int unbounded, কিন্তু Reverse Integer problem ইচ্ছে করে 32-bit সীমা চাপায় — তখন প্রতি ধাপে বা শেষে নিজে check করে 0 ফেরাতে হয়।

শেখা ও পরের অনুশীলন

এই ধারণার পরের অনুশীলন

  • 005 — Palindrome Number (এই repo) — সংখ্যাটা reverse বানিয়ে original-এর সাথে মেলানোই পুরো সমাধান; এটা 004-এর সরাসরি পরের ধাপ।
  • LeetCode — Reverse Integer (https://leetcode.com/problems/reverse-integer/) — হুবহু এই reverse, কিন্তু সাথে 32-bit overflow check; rev * 10 + d লাইনটাই কেন্দ্র।
  • LeetCode — String to Integer (atoi) (https://leetcode.com/problems/string-to-integer-atoi/) — উল্টোমুখী আত্মীয়: digit গুলো বাঁ থেকে পড়ে result = result * 10 + d দিয়ে সংখ্যা বানানো — একই "বামে ঠেলে ডানে বসাও" মন্ত্র।

কৌশলটি মনে রাখুন

Number building via rev * 10 + d — digit থেকে সংখ্যা বানানোর মূল কৌশল, ঠিক digit extraction-এর আয়না। মনে রাখার বড় শিক্ষা: × 10 মানে জায়গা বানানো (বামে ঠেলা), + d মানে নতুন digit বসানো। ডান থেকে তুলে বাঁয়ে বসালে সংখ্যা উল্টে যায়; বাঁ থেকে পড়লে একই সংখ্যা ফিরে আসে — এই দুই ব্যবহারই পরে বারবার ফিরবে।

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

ভবিষ্যতের আমাকে এক লাইনে: "reverse = peeling loop-এ % 10 তুলে rev = rev * 10 + d দিয়ে বানাও, n //= 10 দিয়ে ফেলো। × 10 = জায়গা বানানো, + d = digit বসানো। চিহ্ন আগে আলাদা রেখো; trailing zero মিলিয়ে যাওয়া স্বাভাবিক।"

আগের ধাপ → 002 — Sum of Digits (যেখান থেকে peeling loop পেলাম)। শিকড়ে → 001 — Even or Odd। পরের ধাপ → 005 — Palindrome Number (এই reverse-কে কাজে লাগিয়ে)।

ফিরে দেখা: এই level-এর map → ../README.md · এই note-টা যে ছকে লেখা → ../../../templates/math-problem-note-template.md।


মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" এমন দাবি করা হয়নি — বরং "common interview pattern" লেখা।