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 করে যোগ করি:
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-কাটার কাঁচি চালাই:
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¶
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!")
কোডের প্রতিটি অংশ¶
negative হলে abs() দিয়ে চিহ্ন সরিয়ে দিই, যাতে while n > 0 ঠিকমতো চলে আর digit গুলোই গোনা হয় (চিহ্ন নয়)।
যোগফল রাখার পাত্র, শুরুতে খালি।
যতক্ষণ সংখ্যায় digit বাকি আছে (n এখনো 0 হয়নি) — ততক্ষণ চালাও। কৌটোয় কয়েন আছে কিনা, এই চেক।
last digit তুলে (% 10) যোগফলে জমা করি। (d-তে আলাদা করে রাখা বাধ্যতামূলক নয়, কিন্তু পড়তে সুবিধা।)
সবচেয়ে জরুরি লাইন — last digit ফেলে দিয়ে n-কে এক ঘর ছোট করি। এটা না থাকলে n কখনো 0 হবে না → loop চিরকাল চলবে (infinite loop)।
sum_of_digits_str হলো generator expression — str(abs(n))-এর প্রতিটা অক্ষর int() করে sum() যোগ করে। ছোট আর সুন্দর, কিন্তু ভেতরে string বানায়।
Output মিলিয়ে দেখা¶
চালালে ছাপবে:
তিনটা সংখ্যা তিনটা print থেকে — 1234 → 10, 305 → 8, 99 → 18। তার আগে সব assert চুপচাপ pass করেছে (একটাও fail করলে সেখানেই থেমে error দিত)। শেষ লাইন সব test pass! মানে দুই পদ্ধতির উত্তর মিলেছে, সব ঠিক।
JavaScript ও test cases¶
Python-এ integer division // আছে, JS-এ নেই — তাই খোসা-ছাড়ানোতে Math.floor(n / 10) লাগবে (নিচের টীকা)।
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¶
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-টা মেমরিতে বানায়।)
যে ভুলগুলো এড়াতে হবে¶
n //= 10ভুলে যাওয়া — সবচেয়ে কমন ভুল। ছাড়া পড়লেnএকই থেকে যায়,while n > 0কখনো শেষ হয় না → infinite loop।%আর//উল্টে ফেলা —n % 10digit দেয়,n // 10খোসা ফেলে। উল্টে দিলে সব ভুল। মনে রাখো:%= "যা বাকি",//= "কতবার ঢুকল"।% 2লিখে ফেলা — 001-এর অভ্যাসে অনেকে% 2লেখে; digit চাই বলে% 10লাগবে।- negative ভুলে যাওয়া —
abs(n)না নিলে-1234-এwhile n > 0একবারও চলবে না, ভুল করে 0 ফেরত আসবে। totalবাইরে initialize না করা —total = 0loop-এর ভেতরে বসালে প্রতিবার রিসেট হয়ে যাবে।
শেখা ও পরের অনুশীলন¶
এই ধারণার পরের অনুশীলন¶
এই 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// baseloop দিয়েই এক 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" লেখা।