কনটেন্টে যান

002 — Sum of Digits

001-এ আমরা % দিয়ে last bit ছুঁয়েছিলাম। আজ একই কৌশল সামান্য বদলে — % 10 দিয়ে last digit ছোঁব, আর তার সাথে যোগ হবে আমাদের প্রথম "asol" loop। এই loop-টা পুরো level 0-এর হৃৎপিণ্ড; একবার হাতে এলে অর্ধেক problem এমনিই খুলে যাবে। ধীরে যাও, dry run-টা নিজে হাতে করো।

Problem link ও উৎস

  • Original source: Classic beginner exercise
  • Platform: Classic exercise
  • Topic: Math-based programming fundamentals → Level 0: Absolute Basics
  • Difficulty: Easy
  • Pattern: digit extraction (sum)
  • Basic idea inherited from: 001 (Even or Odd)

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

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

একটা integer n দেওয়া আছে। তার সব digit যোগ করে sum বের করতে হবে।

যেমন 1234 হলে digit গুলো 1, 2, 3, 4 — যোগফল 1 + 2 + 3 + 4 = 10।

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

Input ও output

উদাহরণ
input   ->  output   (কীভাবে)
------------------------------
 1234   ->   10      (1+2+3+4)
    0   ->    0      (digit একটাই: 0, যোগফল 0)
    7   ->    7      (একটাই digit)
   99   ->   18      (9+9)
  305   ->    8      (3+0+5, মাঝের 0-ও ঠিক ধরা পড়ে)
-1234   ->   10      (চিহ্ন বাদ দিয়ে digit গুলোই গোনা)

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

ভাবো একটা পয়সার কৌটো (piggy bank), মুখটা সরু — একসাথে অনেক কয়েন বের হয় না, এক-একটা করে ঝাঁকিয়ে ফেলতে হয়।

  • প্রতিবার ঝাঁকুনিতে নিচ থেকে একটা কয়েন পড়ে → n % 10 (last digit তুলে নেওয়া)
  • সেই কয়েনের মূল্য তোমার মোট গোনায় যোগ করো → total += digit
  • কৌটোয় বাকি কয়েন রয়ে গেল → n //= 10 (একটা কমে গেল)

কৌটো খালি (n = 0) হলে থামো। হাতে যা মোট জমেছে — সেটাই sum of digits। এক-একটা কয়েন এক-একটা digit, আর ঝাঁকুনিই হলো আমাদের loop।

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

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

সরাসরি 001 (Even or Odd) থেকে।

001-এ শিখেছিলাম — পুরো সংখ্যা না দেখে শুধু শেষটা দেখলেই চলে, আর সেই শেষটা ধরিয়ে দেয় % operator (n % 2 last bit)। আজ ঠিক সেই idea, শুধু 2-এর জায়গায় 10:

উদাহরণ
001:  n % 2   -> last bit / even-odd
002:  n % 10  -> last digit  (একই "শেষ ছোঁয়ার" চিন্তা, বড় ভাই)

001-এ আমরা শুধু একবার শেষটা দেখেছিলাম। 002-এ এসে শেষটা দেখি, ফেলে দিই, আবার শেষটা দেখি — মানে একই idea-কে loop-এ বসিয়ে দিলাম। এটাই inheritance: পুরোনো চিন্তা + একটা নতুন মোচড়।

ভেতরের অঙ্ক

দুটো operator-এর জুটি — এদেরই বলি "digit-কাটার কাঁচি":

  • n % 10 → সংখ্যার last digit দেয়। যেমন 1234 % 10 = 4।
  • n // 10 → last digit ফেলে দেওয়া সংখ্যা দেয় (খোসা ছাড়ানো)। যেমন 1234 // 10 = 123।

তাহলে কৌশল: বারবার last digit তুলে যোগফলে জমা করো, তারপর সেই digit ফেলে দাও — যতক্ষণ না সংখ্যাটা 0 হয়ে যায়।

উদাহরণ
total = 0
যতক্ষণ n > 0:
    total += n % 10      # last digit তুলে যোগ করো
    n //= 10             # সেই digit ফেলে দাও

এই while n > 0 কাঠামোটাকে বলে digit extraction loop — মুখস্থ না, বুঝে নাও।

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

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

প্রথমে যেটা মাথায় আসে — সংখ্যাটাকে string বানিয়ে ফেলি, তারপর প্রতিটা অক্ষর আবার int করে যোগ করি:

Python
def sum_of_digits_str(n):
    total = 0
    for ch in str(abs(n)):   # "1234" -> '1','2','3','4'
        total += int(ch)     # প্রতিটা অক্ষরকে সংখ্যা বানিয়ে যোগ
    return total

এটা কাজ করে, ঠিক উত্তর দেয়, পড়তেও সহজ। অনেক সময় এটাই যথেষ্ট। তাহলে সমস্যা কোথায়?

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

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

  • বাড়তি জায়গা: str(n) একটা নতুন string বানায় — মানে O(digit) extra space। arithmetic loop-এ সেটা লাগে না (O(1) space)।
  • type conversion: প্রতিটা অক্ষরে int(ch) ডাকা — ছোট কিন্তু অপ্রয়োজনীয় overhead।
  • ভাষা-নির্ভরতা: Python-এ str() সহজ, কিন্তু C++/Java-তে string-এ যাওয়া-আসা ঝামেলার। arithmetic পদ্ধতি যেকোনো ভাষায় একইভাবে চলে — interview-তে এটা বড় সুবিধা।

মূল কথা: string version দিয়ে উত্তর পাওয়া যায়, কিন্তু এই level-এর আসল লক্ষ্য হলো arithmetic loop-টা আয়ত্ত করা — কারণ পরের problem গুলোতে (reverse, palindrome, armstrong) string দিয়ে চালানো যায় না, ওখানে এই % 10 // 10 loop-ই লাগবে।

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

String বাদ — সরাসরি সংখ্যার উপরেই digit-কাটার কাঁচি চালাই:

Python
def sum_of_digits(n):
    n = abs(n)          # negative হলেও digit একই
    total = 0
    while n > 0:
        total += n % 10   # last digit যোগ করো
        n //= 10          # last digit ফেলো
    return total

কোনো নতুন string নেই, কোনো type conversion নেই — শুধু সংখ্যা আর দুটো operator। জায়গা লাগছে O(1), আর এই loop-টাই হবে তোমার সারা জীবনের সঙ্গী।

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

মাথায় এই ছবিটা গেঁথে নাও — একটা সংখ্যা মানে digit-এর একটা সারি, আর % 10 / // 10 দিয়ে তুমি সেই সারির ডান প্রান্ত থেকে একটা একটা করে খুলছ।

  • % 10 = "ডান প্রান্তের জিনিসটা দেখাও" (পড়ে নাও, সরাও না)
  • // 10 = "ডান প্রান্তেরটা সরিয়ে দাও" (এক ঘর ছোট হলো)

এই দুটো একসাথে = "ডান থেকে বাঁয়ে digit ধরে হাঁটা"। আজ আমরা হাঁটতে হাঁটতে যোগ করছি; কাল 003-এ একই হাঁটায় শুধু গুনব (count); পরে armstrong-এ digit-কে power-এ তুলব। হাঁটাটা এক, শুধু "প্রতি ধাপে কী করব" বদলায়।

ছবিতে বোঝা

1234 থেকে এক-একটা digit বেরিয়ে আসছে, পাশে running total বাড়ছে — frame by frame:

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

frame 1:  n = 1 2 3 [4]   --%10-->  4   total = 0 + 4 = 4
                    ^^^    --//10->  n = 123

frame 2:  n = 1 2 [3]     --%10-->  3   total = 4 + 3 = 7
                  ^^^      --//10->  n = 12

frame 3:  n = 1 [2]       --%10-->  2   total = 7 + 2 = 9
                ^^^        --//10->  n = 1

frame 4:  n = [1]         --%10-->  1   total = 9 + 1 = 10
              ^^^          --//10->  n = 0   (কৌটো খালি, থামো!)

ফলাফল: total = 10

খেয়াল করো — digit গুলো বেরোচ্ছে ডান থেকে বামে (4, 3, 2, 1), কিন্তু যোগফলে ক্রম কোনো ব্যাপার না, তাই sum ঠিকঠাক 10।

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

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

step n (শুরুতে) d = n % 10 total (পরে) n //= 10 (পরে)
1 1234 4 0 + 4 = 4 123
2 123 3 4 + 3 = 7 12
3 12 2 7 + 2 = 9 1
4 1 1 9 + 1 = 10 0

ধাপ 4-এর শেষে n = 0, তাই while n > 0 আর সত্যি নয় — loop থামল। total = 10 — ঠিক 1+2+3+4। নিজে খাতায় এই টেবিলটা আরেকবার বানাও 305 বা 99 দিয়ে, হাত যেন চিনে যায়।

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

Python

Python
def sum_of_digits(n):
    """n-এর সব digit-এর যোগফল (arithmetic পদ্ধতি)।"""
    n = abs(n)              # negative-এর জন্য চিহ্ন বাদ
    total = 0
    while n > 0:
        d = n % 10          # last digit
        total += d          # যোগফলে জমা
        n //= 10            # last digit ফেলে দাও
    return total


def sum_of_digits_str(n):
    """একই উত্তর, string পদ্ধতি (তুলনার জন্য)।"""
    return sum(int(ch) for ch in str(abs(n)))


# --- ছোট test গুলো (সব pass করা উচিত) ---
assert sum_of_digits(1234) == 10
assert sum_of_digits(0) == 0       # 0-এর digit sum 0
assert sum_of_digits(7) == 7
assert sum_of_digits(99) == 18
assert sum_of_digits(305) == 8     # মাঝের 0-ও ধরা পড়ে
assert sum_of_digits(-1234) == 10  # চিহ্ন বাদ
assert sum_of_digits_str(1234) == 10   # দুই পদ্ধতি মেলে

print(sum_of_digits(1234))   # 10
print(sum_of_digits(305))    # 8
print(sum_of_digits(99))     # 18
print("সব test pass!")

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

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

negative হলে abs() দিয়ে চিহ্ন সরিয়ে দিই, যাতে while n > 0 ঠিকমতো চলে আর digit গুলোই গোনা হয় (চিহ্ন নয়)।

উদাহরণ
    total = 0

যোগফল রাখার পাত্র, শুরুতে খালি।

উদাহরণ
    while n > 0:

যতক্ষণ সংখ্যায় digit বাকি আছে (n এখনো 0 হয়নি) — ততক্ষণ চালাও। কৌটোয় কয়েন আছে কিনা, এই চেক।

উদাহরণ
        d = n % 10
        total += d

last digit তুলে (% 10) যোগফলে জমা করি। (d-তে আলাদা করে রাখা বাধ্যতামূলক নয়, কিন্তু পড়তে সুবিধা।)

উদাহরণ
        n //= 10

সবচেয়ে জরুরি লাইন — last digit ফেলে দিয়ে n-কে এক ঘর ছোট করি। এটা না থাকলে n কখনো 0 হবে না → loop চিরকাল চলবে (infinite loop)।

sum_of_digits_str হলো generator expression — str(abs(n))-এর প্রতিটা অক্ষর int() করে sum() যোগ করে। ছোট আর সুন্দর, কিন্তু ভেতরে string বানায়।

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

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

উদাহরণ
10
8
18
সব test pass!

তিনটা সংখ্যা তিনটা print থেকে — 1234 → 10, 305 → 8, 99 → 18। তার আগে সব assert চুপচাপ pass করেছে (একটাও fail করলে সেখানেই থেমে error দিত)। শেষ লাইন সব test pass! মানে দুই পদ্ধতির উত্তর মিলেছে, সব ঠিক।

JavaScript ও test cases

Python-এ integer division // আছে, JS-এ নেই — তাই খোসা-ছাড়ানোতে Math.floor(n / 10) লাগবে (নিচের টীকা)।

JavaScript
function sumOfDigits(n) {
  n = Math.abs(n);            // negative হলেও digit একই
  let total = 0;
  while (n > 0) {
    total += n % 10;         // last digit তুলে যোগ
    n = Math.floor(n / 10);  // JS-এ // নেই → Math.floor দিয়ে integer division
  }
  return total;
}

// ছোট throwing assert — fail করলে এখানেই থেমে error দেবে
const assert = (cond, msg = "") => { if (!cond) throw new Error("FAIL " + msg); };

// ---- test cases ----
assert(sumOfDigits(1234) === 10, "1234");
assert(sumOfDigits(0) === 0, "0");
assert(sumOfDigits(99) === 18, "99");
assert(sumOfDigits(305) === 8, "305");        // মাঝের 0-ও ধরা পড়ে
assert(sumOfDigits(-1234) === 10, "-1234");   // চিহ্ন বাদ
console.log("সব JS test pass!");

JS টীকা: JavaScript-এ floor-division operator নেই; n = Math.floor(n / 10) (অথবা ছোট সংখ্যায় n = (n / 10) | 0) ব্যবহার করো — সরাসরি n / 10 দিলে decimal থেকে যাবে আর loop ভুল চলবে। আর Math.abs আগে নাও, কারণ JS-এ -1234 % 10 === -4।

TypeScript ও test cases

TypeScript
function sumOfDigits(n: number): number {
  n = Math.abs(n);
  let total = 0;
  while (n > 0) {
    total += n % 10;
    n = Math.trunc(n / 10); // Math.trunc = "ভগ্নাংশ ফেলে দাও" — intent পরিষ্কার
  }
  return total;
}

// ---- typed test table: [input, expected] ----
const cases: ReadonlyArray<readonly [number, number]> = [
  [1234, 10], [0, 0], [99, 18], [305, 8], [-1234, 10],
];
for (const [input, want] of cases) {
  const got = sumOfDigits(input);
  if (got !== want) throw new Error(`FAIL ${input}: got ${got}, want ${want}`);
}
console.log("সব TS test pass!");

TS টীকা: test গুলো একটা ReadonlyArray<readonly [number, number]> table-এ রাখলে নতুন case যোগ করা সহজ আর tuple type ভুল-আকারের case compile-time-এই আটকায়। এটাই data-driven / table-driven testing — production test suite-এ খুব কমন।

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

সময়

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

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

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

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

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

  1. n //= 10 ভুলে যাওয়া — সবচেয়ে কমন ভুল। ছাড়া পড়লে n একই থেকে যায়, while n > 0 কখনো শেষ হয় না → infinite loop।
  2. % আর // উল্টে ফেলা — n % 10 digit দেয়, n // 10 খোসা ফেলে। উল্টে দিলে সব ভুল। মনে রাখো: % = "যা বাকি", // = "কতবার ঢুকল"।
  3. % 2 লিখে ফেলা — 001-এর অভ্যাসে অনেকে % 2 লেখে; digit চাই বলে % 10 লাগবে।
  4. negative ভুলে যাওয়া — abs(n) না নিলে -1234-এ while n > 0 একবারও চলবে না, ভুল করে 0 ফেরত আসবে।
  5. total বাইরে initialize না করা — total = 0 loop-এর ভেতরে বসালে প্রতিবার রিসেট হয়ে যাবে।

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

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

এই digit extraction loop-ই নিচের সবগুলোর ভিত্তি:

  • 007 — Digital Root (এই repo) — digit sum বারবার নাও যতক্ষণ না এক digit থাকে (9875 → 29 → 11 → 2)। এটাই digit sum-এর সরাসরি বড় ভাই; এর সাথে আছে % 9-এর জাদু। সম্পর্কিত: LeetCode — Add Digits (https://leetcode.com/problems/add-digits/)।
  • 006 — Armstrong Number (এই repo) — একই loop, কিন্তু digit যোগ না করে digit ** k যোগ করো।
  • LeetCode — Happy Number (https://leetcode.com/problems/happy-number/) — এখানে digit-এর বর্গের যোগফল বারবার নিতে হয়; ভেতরের ইঞ্জিন সেই digit extraction loop।

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

Digit extraction loop — while n > 0: d = n % 10; (d-কে কাজে লাগাও); n //= 10। এটাই level 0-এর সবচেয়ে দরকারি, সবচেয়ে বেশিবার-ফেরা pattern। আজ "কাজে লাগাও" = যোগ করা; কাল শুধু এই এক লাইন বদলে count, power-sum, reverse — সব হবে। loop-টা একবার চোখ বন্ধ করে লিখতে পারলে অর্ধেক যুদ্ধ জেতা।

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

ভবিষ্যতের আমাকে এক লাইনে: "digit sum = while loop-এ n % 10 তুলে যোগ করো, n //= 10 দিয়ে ফেলে দাও, n = 0 হলে থামো। 001-এর last-digit idea-ই এখানে loop হয়ে ফিরেছে — আর এই loop-টাই পরের সব digit problem-এর মা।"

আগের ধাপ → 001 — Even or Odd। পরের ধাপ → 003 — Count Digits (একই loop, এবার গুনব)। আরও দূরে → 007 — Digital Root।

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

কোথায় কাজে লাগে

Digit extraction (% 10 / / 10) loop-টা অসংখ্য জায়গায় লুকিয়ে থাকে:

  • Checksum / validation: Luhn algorithm (credit/debit card নম্বর যাচাই) মূলত digit-sum-ভিত্তিক; ISBN, IMEI-তেও digit-sum check।
  • Cast-out-nines: হিসাব দ্রুত যাচাই করতে digit sum mod 9 (পরের problem digital-root-এর ভিত্তি)।
  • Hashing / bucketing: সরল hash-এ digit sum দিয়ে bucket বাছা।
  • Number formatting: currency/locale formatting-এ digit ধরে ধরে process।
  • Base conversion: ঠিক এই % base / / base loop দিয়েই এক base থেকে আরেক base-এ সংখ্যা রূপান্তর।

মূল pattern digit extraction loop — একবার হাতে এলে reverse, palindrome, base-conversion, happy-number সব এর উপরেই দাঁড়ায়।


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