003 — Count Digits¶
002-এ তুমি digit extraction loop শিখেছ — সেই কষ্টের ফল এবার তুলে নেওয়ার পালা। আজকের problem-এ একই loop, প্রায় একই code, শুধু "প্রতি ধাপে কী করব" অংশটা বদলাবে। এটাই inheritance-এর মজা: নতুন problem, পুরোনো হাতিয়ার। সাথে একটা ছোট কিন্তু দামি ফাঁদ আছে (
n = 0) — সেটা না দেখলে interview-তে ধরা খাবে।
Source¶
- Original source: Classic beginner exercise
- Platform: Classic exercise
- Topic: Math-based programming fundamentals → Level 0: Absolute Basics
- Difficulty: Easy
- Pattern: digit extraction (count)
- Basic idea inherited from: 002 (Sum of Digits)
1. Problem in simple words¶
একটা integer n দেওয়া আছে। তাতে কয়টা digit আছে — সেই সংখ্যাটা বের করতে হবে।
যেমন 1234-এ digit চারটে (1, 2, 3, 4), তাই উত্তর 4। 7-এ একটাই, উত্তর 1।
2. What is the math idea?¶
মূল চিন্তা 002-এর হুবহু সেই loop — % 10 দিয়ে last digit দেখা, // 10 দিয়ে ফেলে দেওয়া। শুধু এবার digit-গুলো যোগ না করে, কতবার ফেললাম সেটা গুনি:
এর বাইরে দুটো shortcut-ও আছে (পরে আলোচনা করব):
len(str(n))— সংখ্যাকে string বানিয়ে দৈর্ঘ্য মাপাmath.floor(math.log10(n)) + 1— খাঁটি গণিত, কোনো loop ছাড়াই
3. Which basic idea does this inherit from?¶
সরাসরি 002 (Sum of Digits) থেকে — আসলে এটা 002-এর প্রায় যমজ।
002-এর loop আর 003-এর loop পাশাপাশি রাখো:
002 (sum): while n > 0: total += n % 10 ; n //= 10
003 (count): while n > 0: count += 1 ; n //= 10
^^^^^^^^^^^^
শুধু এই এক লাইন আলাদা
দেখো — peeling loop (n //= 10 দিয়ে খোসা ছাড়ানো) হুবহু এক। 002-এ প্রতি digit-এর মান জমিয়েছিলাম; এখানে শুধু কতবার ঘুরলাম গুনছি। এটাই বুঝিয়ে দেয়, একটা loop শিখে ফেললে কত problem এমনিই হাতে চলে আসে।
4. Real-life analogy¶
ভাবো একটা সিঁড়ি, আর তুমি অন্ধকারে নামছ — কয়টা ধাপ আছে জানো না।
- প্রতিবার এক ধাপ নামলে মনে মনে গোনো: "এক... দুই... তিন..." →
count += 1 - এক ধাপ নিচে নামা =
n //= 10(সংখ্যা এক digit ছোট হলো) - মাটিতে পা পড়ল (
n = 0) → থামো; এতক্ষণ যত গুনলে, সিঁড়িতে তত ধাপ
digit গোনা আর সিঁড়ির ধাপ গোনা — একই ব্যাপার: প্রতি ধাপে এক বাড়াও, তল ছুঁলে থামো।
5. Visual explanation¶
1234-এ peeling চলছে, পাশে counter একটা একটা করে টিক টিক করে বাড়ছে:
শুরুতে: n = 1234, count = 0
step 1: n = 1234 --//10--> 123 count: 0 -> 1
step 2: n = 123 --//10--> 12 count: 1 -> 2
step 3: n = 12 --//10--> 1 count: 2 -> 3
step 4: n = 1 --//10--> 0 count: 3 -> 4 (n = 0, থামো!)
মোট কতবার //10 করলাম = 4 -> digit সংখ্যা 4
এক বাক্যে মনে রাখো: যতবার // 10 করে 0-তে পৌঁছালে, ততগুলোই digit।
6. Example input and output¶
input -> output (কেন)
---------------------------
1234 -> 4
7 -> 1 (একটাই digit)
0 -> 1 <-- সাবধান! 0-এরও কিন্তু একটা digit
1000 -> 4 (পেছনের শূন্যগুলোও digit)
99 -> 2
-55 -> 2 (চিহ্ন বাদ, digit দুটো)
ওই 0 -> 1 লাইনটাই এই problem-এর সবচেয়ে দামি পরীক্ষা। ভেবে দেখো — খালি loop কিন্তু এখানে ভুল করবে (নিচে section 9 আর 17-এ ব্যাখ্যা)।
7. Brute force thinking¶
সবচেয়ে সৎ, "হাতে গুনে" পদ্ধতি — সেই চেনা peeling loop চালিয়ে এক একটা digit ফেলো আর গোনো:
def count_digits_loop(n):
n = abs(n)
if n == 0:
return 1 # 0-এর জন্য আলাদা যত্ন
count = 0
while n > 0:
count += 1
n //= 10
return count
এটা পরিষ্কার, যেকোনো ভাষায় চলে, আর 002-এর loop জানলে এক মিনিটেই লেখা যায়। digit সংখ্যা মুষ্টিমেয়, তাই এটা মোটেও ধীর নয় — এটাই বেশিরভাগ সময়ের সেরা উত্তর।
8. Why brute force may be slow¶
সত্যি বলতে এই loop "ধীর" নয় — O(log n), মানে 9-digit সংখ্যাতেও মাত্র 9 ধাপ। তাহলে আরও ভাবার দরকার কেন?
দুটো কারণে এটা জানা ভালো:
- শুধু গোনাই যখন দরকার: আমরা digit-গুলোর মান নিয়ে কিছু করছি না, শুধু কয়টা জানতে চাই। এমন "শুধু সংখ্যা চাই" ক্ষেত্রে loop না চালিয়ে এক ধাপে (formula দিয়ে) উত্তর পাওয়া আরও পরিচ্ছন্ন।
- বারবার ডাকা হলে: কোনো বড় program-এর ভেতরের loop-এ যদি লক্ষ-লক্ষ বার digit গুনতে হয়, তখন প্রতি বার ছোট loop চালানোর চেয়ে সরাসরি
len(str(n))বা formula দ্রুত মনে হয়।
মূল শিক্ষা ধীরগতি নয় — একই জিনিস কয়েকভাবে দেখতে পারা। চলো সেই বিকল্পগুলো দেখি।
9. Better thinking¶
দুটো ছোট বিকল্প, যেগুলো interview-তে জানা থাকলে নম্বর কাটে না:
(ক) string-এর দৈর্ঘ্য — Python নিজেই digit গুনে দেয়:
ছোট, সুন্দর, আর মজার ব্যাপার — 0-এর জন্যও আলাদা যত্ন লাগে না, কারণ str(0) হলো "0", দৈর্ঘ্য 1।
(খ) log10 formula — খাঁটি গণিত, কোনো loop ছাড়াই:
কেন কাজ করে? কারণ log10 বলে দেয় সংখ্যাটা "কত শূন্যের ঘরে"। 1234-এর log10 ≈ 3.09, তার floor 3, +1 = 4। 1000-এর log10 = 3, floor 3, +1 = 4। অর্থাৎ digit সংখ্যা = floor(log10(n)) + 1। এটা O(1) — একদম এক ধাপ।
(সতর্কতা: log10 floating-point দিয়ে চলে, তাই 10-এর বড় power-এ মাঝে মাঝে সামান্য ভুল করতে পারে — তাই নিরাপদ পছন্দ এখনো সেই loop। নিচে common mistakes-এ এই ফাঁদটা আছে।)
10. Thinking tweak¶
মূল মোচড় এক বাক্যে: যতবার // 10 করে 0-তে পৌঁছাও, ততগুলো digit।
002-এ আমরা peeling-এর সময় digit-এর মান কুড়িয়েছিলাম। এখানে মান নিয়ে মাথা ঘামাই না — শুধু পদক্ষেপ গুনি। একই হাঁটা, শুধু এবার পা ফেলার সংখ্যাটাই আমাদের উত্তর। তাই 002-এর loop থেকে শুধু total += n % 10 সরিয়ে count += 1 বসিয়ে দাও — ব্যস।
11. Step-by-step dry run¶
n = 1234। এখানে digit-এর মান অপ্রয়োজনীয়, তাই টেবিলে শুধু n আর count দেখছি:
| step | n (শুরুতে) | count (পরে) | n //= 10 (পরে) |
|---|---|---|---|
| 1 | 1234 | 0 + 1 = 1 | 123 |
| 2 | 123 | 1 + 1 = 2 | 12 |
| 3 | 12 | 2 + 1 = 3 | 1 |
| 4 | 1 | 3 + 1 = 4 | 0 |
ধাপ 4-এর পর n = 0, loop থামল। count = 4 — ঠিক চারটে digit।
এবার মনে মনে n = 0 চালাও: abs(0) = 0, while 0 > 0 কখনো সত্যি নয়, তাই loop একবারও চলবে না, count থেকে যাবে 0 — অথচ সঠিক উত্তর 1! এজন্যই code-এ আগে থেকে if n == 0: return 1 বসাতে হয়। এই ছোট dry run-টাই বাগটা ধরিয়ে দেয়।
12. Python solution¶
import math
def count_digits(n):
"""n-এ কয়টা digit (arithmetic পদ্ধতি)।"""
n = abs(n)
if n == 0:
return 1 # 0-এর digit সংখ্যা 1 — আলাদা handle
count = 0
while n > 0:
count += 1
n //= 10
return count
def count_digits_str(n):
"""একই উত্তর, string পদ্ধতি (0-ও এমনিই ঠিক)।"""
return len(str(abs(n)))
def count_digits_log(n):
"""একই উত্তর, log10 formula (O(1), কিন্তু float-সতর্কতা)।"""
if n == 0:
return 1
return math.floor(math.log10(abs(n))) + 1
# --- ছোট test গুলো (সব pass করা উচিত) ---
assert count_digits(1234) == 4
assert count_digits(0) == 1 # সবচেয়ে দামি edge case
assert count_digits(7) == 1
assert count_digits(1000) == 4
assert count_digits(99) == 2
assert count_digits(-55) == 2 # চিহ্ন বাদ
assert count_digits_str(0) == 1
assert count_digits_str(1234) == 4
assert count_digits_log(1234) == 4 # power-of-10 নয়, তাই নিরাপদ
print(count_digits(1234)) # 4
print(count_digits(0)) # 1
print(count_digits(1000)) # 4
print("সব test pass!")
13. Line-by-line explanation¶
চিহ্ন সরিয়ে দিই, যাতে negative সংখ্যাতেও digit গোনা ঠিক হয় (-55 → 55)।
এই problem-এর প্রাণভোমরা। 0-এর জন্য loop চলবেই না, তাই আগেভাগে 1 ফেরত দিই। এই দুই লাইন বাদ দিলে count_digits(0) ভুল করে 0 দেবে।
চেনা peeling loop। প্রতিবার একটা digit ফেলছি (n //= 10) আর গোনায় এক বাড়াচ্ছি (count += 1)। n = 0 হলে থামে।
str(abs(n)) সংখ্যাকে string বানায়, len() তার অক্ষর গোনে = digit সংখ্যা। 0 → "0" → দৈর্ঘ্য 1, তাই এখানে আলাদা if লাগে না।
log10 দিয়ে "কত ঘরের সংখ্যা" বের করে floor নিয়ে +1। দ্রুত, কিন্তু float বলে 10-এর বড় power-এ সাবধান (তাই এখানেও if n == 0 আলাদা)।
14. Output walkthrough¶
চালালে ছাপবে:
তিনটা সংখ্যা তিনটা print থেকে: count_digits(1234) → 4, count_digits(0) → 1 (edge case ঠিকঠাক!), count_digits(1000) → 4। আগের সব assert নিঃশব্দে pass করেছে; শেষ লাইন সব test pass! মানে তিন পদ্ধতির উত্তরই মিলেছে।
15. Time complexity¶
O(log₁₀ n) — loop আর log version দুটোই। loop প্রতি ধাপে n-কে 10 দিয়ে ভাগ করে, তাই digit সংখ্যার সমান ঘোরে, আর digit সংখ্যা ≈ log₁₀ n। log10 formula তো আরও সরাসরি — গণিতের চোখে O(1)। str version-ও O(digit), কারণ string বানাতে সব digit পড়তে হয়।
16. Space complexity¶
- loop version: O(1) — শুধু
count,n; বাড়তি কিছু না। - log version: O(1)।
- str version: O(log n) — কারণ পুরো string-টা মেমরিতে তৈরি হয়।
interview-তে সাধারণত loop version-ই বলবে: O(log n) time, O(1) space।
17. Common mistakes¶
n = 0ভুলে যাওয়া — এই problem-এর এক নম্বর ফাঁদ। খালি loop 0-এর জন্য 0 ফেরত দেয়, কিন্তু সঠিক উত্তর 1। আগেif n == 0: return 1বসাও।n //= 10ভুলে যাওয়া — তাহলেwhile n > 0কখনো থামবে না → infinite loop।- log10-এর floating-point ফাঁদ —
math.log10(1000)যদি কোনো সিস্টেমে 2.9999999 আসে, floor দিয়ে 2 হয়ে উত্তর ভুল (3) হয়ে যাবে। বড় power-of-10-এ ঝুঁকি থাকে; নিরাপদ থাকতে loop বাlen(str(n))ব্যবহার করো। - negative ভুলে যাওয়া —
abs()না নিলে-55-এ loop চলবে না (while n > 0মিথ্যা)। - count যোগ আর digit যোগ গুলিয়ে ফেলা — এটা sum (002) নয়; এখানে
count += 1, কখনোইcount += n % 10নয়।
18. Harder problems that inherit this idea¶
- 009 — Power of 10 Check (এই repo) — বারবার
// 10করে দেখা সংখ্যাটা ঠিক 10-এর power কিনা; digit গোনার peeling loop-এরই আত্মীয়। - LeetCode — Add Digits (https://leetcode.com/problems/add-digits/) — digit নিয়ে কাজ; digit গোনা/যোগ করার একই extraction ভিত্তি।
- LeetCode — Nth Digit (https://leetcode.com/problems/nth-digit/) — এখানে "কোন সংখ্যায় কয়টা digit" হিসাব করেই ঠিক করতে হয় n-তম digit কোথায় — digit counting-এর সরাসরি প্রয়োগ।
19. Pattern learned¶
একই extraction loop, কাজটা বদলাও। 002-এ digit যোগ করেছিলাম, 003-এ digit গুনলাম — loop এক, শুধু ভেতরের এক লাইন আলাদা। সাথে দুটো করে শেখা: (১) প্রায়ই একই কাজ loop, string, বা formula — তিন রাস্তায় করা যায়; (২) edge case (এখানে n = 0) আগে ভাবলে interview-তে ছোট ভুলে বড় দাম দিতে হয় না।
20. Final summary¶
ভবিষ্যতের আমাকে এক লাইনে: "count digits = 002-এর peeling loop, শুধু count += 1; n = 0 হলে আলাদা করে 1 ফেরাও। চাইলে len(str(n)) বা floor(log10 n)+1-ও চলে — কিন্তু loop সবচেয়ে নিরাপদ।"
আগের ধাপ → 002 — Sum of Digits (যেখান থেকে এই loop পেলাম)। শিকড়ে → 001 — Even or Odd। পরের আত্মীয় → 009 — Power of 10 Check।
ফিরে দেখা: এই level-এর map → ../README.md · এই note-টা যে ছকে লেখা → ../../../templates/math-problem-note-template.md।
21. JavaScript solution (with test cases)¶
function countDigits(n) {
n = Math.abs(n);
if (n === 0) return 1; // edge case: 0-এরও একটা digit
let count = 0;
while (n > 0) {
count++;
n = Math.floor(n / 10);
}
return count;
}
// O(1) বিকল্প (n > 0): Math.floor(Math.log10(Math.abs(n))) + 1
// ⚠️ খুব বড় সংখ্যায় floating-point error দিতে পারে, তাই loop নিরাপদ।
// ছোট throwing assert — fail করলে এখানেই থেমে error দেবে
const assert = (cond, msg = "") => { if (!cond) throw new Error("FAIL " + msg); };
// ---- test cases ----
assert(countDigits(1234) === 4, "1234");
assert(countDigits(0) === 1, "0 -> 1"); // সবচেয়ে দামি case
assert(countDigits(1000) === 4, "1000");
assert(countDigits(-55) === 2, "-55");
console.log("সব JS test pass!");
JS টীকা:
0-এর জন্য আলাদাreturn 1না দিলে loop একবারও চলবে না আর ভুল করে0ফেরত আসবে।log10দিয়ে O(1)-তে করা যায়, কিন্তু বড় সংখ্যায় floating-point precision হারায় — তাই interview-তে loop version নিরাপদ choice।
22. TypeScript solution (with test cases)¶
function countDigits(n: number): number {
n = Math.abs(n);
if (n === 0) return 1;
let count = 0;
while (n > 0) {
count++;
n = Math.trunc(n / 10);
}
return count;
}
const cases: ReadonlyArray<readonly [number, number]> = [
[1234, 4], [0, 1], [1000, 4], [-55, 2],
];
for (const [input, want] of cases) {
if (countDigits(input) !== want) throw new Error(`FAIL ${input}`);
}
console.log("সব TS test pass!");
TS টীকা: edge case (
0 -> 1) test table-এ স্পষ্ট রাখা হলো — high-end code-এ "tricky edge গুলো test-এ লেখা থাকে যাতে refactor-এ ভুল হলে সাথে সাথে ধরা পড়ে"। এটাই regression test-এর মূল ভাবনা।
23. কোথায় কাজে লাগে (real-world use)¶
Digit count (সংখ্যার দৈর্ঘ্য) বাস্তবে যেখানে লাগে:
- Input validation: phone number, card, PIN, OTP-এর দৈর্ঘ্য ঠিক আছে কিনা যাচাই।
- Fixed-width display / padding: number-কে ডানে align বা leading-zero pad করতে কত digit জানা দরকার (invoice, receipt, ID)।
- Pagination / layout: কত digit-এর page number, সেই অনুযায়ী column width।
- Axis scaling: chart-এ tick/label-এর জন্য মান কত "বড়" (log10 ≈ digit count) বুঝে scale।
- Storage estimation: ID/counter কত digit হবে → কত byte/column width লাগবে database schema-তে।
মূল idea: একটা সংখ্যার digit সংখ্যা ≈ log₁₀(n) — এই log-নির্ভর "আকার" intuition scaling, complexity, capacity planning সবখানে ফিরে আসে।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" এমন দাবি করা হয়নি — বরং "common interview pattern" লেখা।