# 003 — Count Digits

> 002-এ তুমি digit extraction loop শিখেছ — সেই কষ্টের ফল এবার তুলে নেওয়ার পালা। আজকের problem-এ একই loop, প্রায় একই code, শুধু "প্রতি ধাপে কী করব" অংশটা বদলাবে। এটাই inheritance-এর মজা: নতুন problem, পুরোনো হাতিয়ার। সাথে একটা ছোট কিন্তু দামি ফাঁদ আছে (`n = 0`) — সেটা না দেখলে interview-তে ধরা খাবে।

<div class="problem-source" markdown="1">

<p class="source-label" id="source">Problem link ও উৎস</p>

- 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)

</div>

## প্রশ্ন ও উদাহরণ {#problem}

### সহজ করে প্রশ্ন {#1-problem-in-simple-words}

একটা integer `n` দেওয়া আছে। তাতে **কয়টা digit আছে** — সেই সংখ্যাটা বের করতে হবে।

যেমন `1234`-এ digit চারটে (1, 2, 3, 4), তাই উত্তর `4`। `7`-এ একটাই, উত্তর `1`।

### Input ও output {#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-এ ব্যাখ্যা)।

### চেনা উদাহরণে বোঝা {#4-real-life-analogy}

ভাবো একটা **সিঁড়ি**, আর তুমি অন্ধকারে নামছ — কয়টা ধাপ আছে জানো না।

- প্রতিবার এক ধাপ নামলে মনে মনে গোনো: "এক... দুই... তিন..." → `count += 1`
- এক ধাপ নিচে নামা = `n //= 10` (সংখ্যা এক digit ছোট হলো)
- মাটিতে পা পড়ল (`n = 0`) → থামো; এতক্ষণ যত গুনলে, সিঁড়িতে তত ধাপ

digit গোনা আর সিঁড়ির ধাপ গোনা — একই ব্যাপার: প্রতি ধাপে এক বাড়াও, তল ছুঁলে থামো।

## ভিত্তির ধারণা {#idea}

### আগের কোন ধারণাটি লাগবে {#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 এমনিই হাতে চলে আসে।

### ভেতরের অঙ্ক {#2-what-is-the-math-idea}

মূল চিন্তা 002-এর হুবহু সেই loop — `% 10` দিয়ে last digit দেখা, `// 10` দিয়ে ফেলে দেওয়া। শুধু এবার digit-গুলো **যোগ** না করে, কতবার ফেললাম সেটা **গুনি**:

```
count = 0
যতক্ষণ n > 0:
    count += 1     # একটা digit ফেলছি, তাই গোনায় এক বাড়ল
    n //= 10
```

এর বাইরে দুটো shortcut-ও আছে (পরে আলোচনা করব):

- `len(str(n))` — সংখ্যাকে string বানিয়ে দৈর্ঘ্য মাপা
- `math.floor(math.log10(n)) + 1` — খাঁটি গণিত, কোনো loop ছাড়াই

## ধাপে ধাপে সমাধান {#walkthrough}

### প্রথম সরল উপায় {#7-brute-force-thinking}

সবচেয়ে সৎ, "হাতে গুনে" পদ্ধতি — সেই চেনা peeling loop চালিয়ে এক একটা digit ফেলো আর গোনো:

```python
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 গুনে দেয়:

```python
def count_digits_str(n):
    return len(str(abs(n)))     # "1234" -> 4 ; "0" -> 1 (এমনিই ঠিক!)
```

ছোট, সুন্দর, আর মজার ব্যাপার — `0`-এর জন্যও আলাদা যত্ন লাগে না, কারণ `str(0)` হলো `"0"`, দৈর্ঘ্য 1।

**(খ) log10 formula** — খাঁটি গণিত, কোনো loop ছাড়াই:

```python
import math

def count_digits_log(n):
    if n == 0:
        return 1
    return math.floor(math.log10(abs(n))) + 1
```

কেন কাজ করে? কারণ `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` বসিয়ে দাও — ব্যস।

### ছবিতে বোঝা {#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।**

### ছোট input-এ ধাপে ধাপে {#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-টাই বাগটা ধরিয়ে দেয়।

## কোড ও ব্যাখ্যা {#code}

### Python {#12-python-solution}

```python
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}

```
    n = abs(n)
```

চিহ্ন সরিয়ে দিই, যাতে negative সংখ্যাতেও digit গোনা ঠিক হয় (`-55` → `55`)।

```
    if n == 0:
        return 1
```

**এই problem-এর প্রাণভোমরা।** 0-এর জন্য loop চলবেই না, তাই আগেভাগে 1 ফেরত দিই। এই দুই লাইন বাদ দিলে `count_digits(0)` ভুল করে 0 দেবে।

```
    count = 0
    while n > 0:
        count += 1
        n //= 10
```

চেনা peeling loop। প্রতিবার একটা digit ফেলছি (`n //= 10`) আর গোনায় এক বাড়াচ্ছি (`count += 1`)। n = 0 হলে থামে।

```
def count_digits_str(n):
    return len(str(abs(n)))
```

`str(abs(n))` সংখ্যাকে string বানায়, `len()` তার অক্ষর গোনে = digit সংখ্যা। `0` → `"0"` → দৈর্ঘ্য 1, তাই এখানে আলাদা if লাগে না।

```
def count_digits_log(n):
    ...
    return math.floor(math.log10(abs(n))) + 1
```

`log10` দিয়ে "কত ঘরের সংখ্যা" বের করে floor নিয়ে `+1`। দ্রুত, কিন্তু float বলে 10-এর বড় power-এ সাবধান (তাই এখানেও `if n == 0` আলাদা)।

### Output মিলিয়ে দেখা {#14-output-walkthrough}

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

```
4
1
4
সব test pass!
```

তিনটা সংখ্যা তিনটা `print` থেকে: `count_digits(1234)` → 4, `count_digits(0)` → 1 (edge case ঠিকঠাক!), `count_digits(1000)` → 4। আগের সব `assert` নিঃশব্দে pass করেছে; শেষ লাইন `সব test pass!` মানে তিন পদ্ধতির উত্তরই মিলেছে।

### JavaScript ও test cases {#21-javascript-solution-with-test-cases}

```javascript
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।

### TypeScript ও test cases {#22-typescript-solution-with-test-cases}

```typescript
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-এর মূল ভাবনা।

## খরচ ও সাধারণ ভুল {#cost}

### সময় {#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}

1. **`n = 0` ভুলে যাওয়া** — এই problem-এর এক নম্বর ফাঁদ। খালি loop 0-এর জন্য 0 ফেরত দেয়, কিন্তু সঠিক উত্তর 1। আগে `if n == 0: return 1` বসাও।
2. **`n //= 10` ভুলে যাওয়া** — তাহলে `while n > 0` কখনো থামবে না → infinite loop।
3. **log10-এর floating-point ফাঁদ** — `math.log10(1000)` যদি কোনো সিস্টেমে 2.9999999 আসে, floor দিয়ে 2 হয়ে উত্তর ভুল (3) হয়ে যাবে। বড় power-of-10-এ ঝুঁকি থাকে; নিরাপদ থাকতে loop বা `len(str(n))` ব্যবহার করো।
4. **negative ভুলে যাওয়া** — `abs()` না নিলে `-55`-এ loop চলবে না (`while n > 0` মিথ্যা)।
5. **count যোগ আর digit যোগ গুলিয়ে ফেলা** — এটা sum (002) নয়; এখানে `count += 1`, কখনোই `count += n % 10` নয়।

## শেখা ও পরের অনুশীলন {#practice}

### এই ধারণার পরের অনুশীলন {#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](002-sum-of-digits.md) (যেখান থেকে এই loop পেলাম)। শিকড়ে → [001 — Even or Odd](001-even-or-odd.md)। পরের আত্মীয় → [009 — Power of 10 Check](009-power-of-10-check.md)।

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

### কোথায় কাজে লাগে {#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" লেখা।
