Skip to content

010 — Number Pattern Printing

এতদিন আমরা একটা সংখ্যা ভেঙে digit নিয়ে খেলছিলাম। আজ একদম আলাদা শাখা — nested loop-এর হাতেখড়ি। সংখ্যার ভেতরে ঢোকা নয়, বরং সারি-সারি করে সংখ্যা সাজিয়ে ছাপা। দেখতে নিরীহ ত্রিভুজ, কিন্তু এই "বাইরের loop সারি, ভেতরের loop কলাম" কাঠামোটাই পরে matrix, grid, 2D DP — সব জায়গায় ফিরবে। ধীরে যাও, প্রতিটা pattern নিজে হাতে খাতায় এঁকে দেখো।

Source

  • Original source: Classic beginner exercise (HackerRank-style warmup)
  • Platform: Classic exercise
  • Topic: Math-based programming fundamentals → Level 0: Absolute Basics
  • Difficulty: Easy
  • Pattern: nested loops
  • Basic idea inherited from: — (এটা আলাদা শাখার শুরু, আগের digit-problem থেকে আসেনি)

1. Problem in simple words

একটা সংখ্যা n দেওয়া আছে। তোমাকে nটা সারি (row) ছাপতে হবে, যেখানে সংখ্যাগুলো একটা সুন্দর pattern বানায়। সবচেয়ে চেনা pattern — একটা ত্রিভুজ:

1
1 2
1 2 3
1 2 3 4

মানে: ১ম সারিতে 1টা সংখ্যা, ২য় সারিতে 2টা, ... i-তম সারিতে iটা সংখ্যা। ব্যস, এই pattern-টা তৈরি করাই কাজ।

(আরও কয়েকটা variation আছে — একই সংখ্যা বারবার, উল্টো ত্রিভুজ — নিচে দেখব। মূল কৌশল সবগুলোতেই এক।)

2. What is the math idea?

এখানে গভীর কোনো গণিত-সূত্র নেই; মূল idea হলো nested loop — একটা loop-এর ভেতরে আরেকটা loop:

  • বাইরের loop ঠিক করে কোন সারি-তে আছি (i = 1 থেকে n)
  • ভেতরের loop ঠিক করে ওই সারিতে কোন কোন সংখ্যা/কলাম ছাপব (j = 1 থেকে i)
বাইরের loop i: 1 .. n          # প্রতিটা সারি
    ভেতরের loop j: 1 .. i      # ওই সারির ভেতরের সংখ্যাগুলো
        ছাপো j
    সারি শেষ -> newline

ছোট গাণিতিক সত্য পাশে: মোট কতগুলো সংখ্যা ছাপা হলো? 1 + 2 + 3 + ... + n = n(n+1)/2 — এটাই বলে দেয় কাজের পরিমাণ (Section 15-এ লাগবে)।

3. Which basic idea does this inherit from?

এই problem-টা digit-extraction পরিবারের নয় — তাই "inherited from: —"। এটা Level 0-এর আলাদা একটা শাখার শিকড়: loop-এর ভেতরে loop।

আগের problem-গুলোতে (002-009) একটাই loop দিয়ে একটা সংখ্যার digit ধরে হাঁটতাম। এখানে উল্টো — সংখ্যা ভাঙছি না, বরং দুটো loop একসাথে চালিয়ে একটা দ্বিমাত্রিক (2D) আকৃতি বানাচ্ছি। তাই এটা নতুন একটা হাতিয়ার, যেটা থেকে পরে গজাবে: matrix ছাপা, grid-এ হাঁটা, multiplication table, এমনকি 2D dynamic programming-এর ভিত।

4. Real-life analogy

ভাবো একটা বইয়ের তাক (bookshelf) সাজাচ্ছ — কয়েকটা থাক (shelf), প্রতিটা থাকে কিছু বই।

  • বাইরের loop = "এখন কোন থাকে কাজ করছি" (থাক 1, থাক 2, ...) → সারি
  • ভেতরের loop = "এই থাকে এক-একটা করে বই বসাচ্ছি" → ওই সারির সংখ্যাগুলো
  • এক থাক ভরা শেষ হলে পরের থাকে যাও = সারি শেষে newline

ত্রিভুজে নিয়মটা শুধু এই — প্রথম থাকে 1টা বই, দ্বিতীয় থাকে 2টা, ... যত নিচে নামো তত বেশি বই। থাক (সারি) সামলায় বাইরের loop, বই (সংখ্যা) সামলায় ভেতরের loop।

5. Visual explanation

n = 4-এর ত্রিভুজ — কোন সারিতে i কত, ভেতরের loop j কতদূর যায়, পাশে দেখো:

i = 1 :  j: 1               ->  "1"          (1টা সংখ্যা)
i = 2 :  j: 1 2             ->  "1 2"        (2টা সংখ্যা)
i = 3 :  j: 1 2 3           ->  "1 2 3"      (3টা সংখ্যা)
i = 4 :  j: 1 2 3 4         ->  "1 2 3 4"    (4টা সংখ্যা)

লক্ষ করো: ভেতরের loop j সবসময় 1 থেকে শুরু,
          কিন্তু থামে i-তে -> তাই সারি যত নিচে, তত লম্বা

আরেকভাবে দেখো — কাজটা আসলে একটা ত্রিভুজাকার grid ভরা:

       j=1  j=2  j=3  j=4
i=1 :  [1]
i=2 :  [1] [2]
i=3 :  [1] [2] [3]
i=4 :  [1] [2] [3] [4]
       ^ ভেতরের loop ডানে বাড়ে, বাইরের loop নিচে নামে

6. Example input and output

input n = 4   ->   output:
1
1 2
1 2 3
1 2 3 4

input n = 1   ->   output:
1

input n = 0   ->   output:
(কিছুই না — খালি, কোনো সারি নেই)

Edge case দুটো: n = 1 → শুধু একটা সারি 1; n = 0 (বা negative) → একটাও সারি নয়, output সম্পূর্ণ খালি। loop ঠিকভাবে লিখলে এগুলো এমনিই সামলে যায়।

7. Brute force thinking

এখানে "brute force বনাম optimal" ব্যাপারটা একটু আলাদা — কারণ pattern ছাপতে হলে প্রতিটা সংখ্যা ছাপতেই হবে, তাকানোর শর্টকাট নেই। তবে প্রথম, সবচেয়ে সরল ভাবনা — প্রতি সারিতে ভেতরের loop দিয়ে এক-একটা সংখ্যা আলাদা করে print করা:

def print_triangle_brute(n):
    for i in range(1, n + 1):       # প্রতিটা সারি
        for j in range(1, i + 1):   # সারির ভেতরের সংখ্যা
            print(j, end=" ")       # একটা একটা ছাপো, পাশে ফাঁকা
        print()                     # সারি শেষ -> পরের লাইনে যাও

এটা একদম সরাসরি pattern-টাকে code-এ অনুবাদ করে — দুটো loop, ভেতরে print। কাজ করে, পড়তেও সহজ।

8. Why brute force may be slow

এই version "ধীর" নয় algorithm-এর অর্থে — মোট n(n+1)/2টা সংখ্যা ছাপতে হলে, সেটুকু কাজ যেকোনো পদ্ধতিতেই লাগবে। কিন্তু কীভাবে ছাপছি, তাতে একটা ব্যবহারিক পার্থক্য আছে:

  • বারবার print() ডাকা ব্যয়বহুল: প্রতিটা সংখ্যার জন্য আলাদা print(j, end=" ") ডাকা — output operation প্রতিবার একটু overhead নেয়। সংখ্যা অনেক হলে (বড় n) এটা টের পাওয়া যায়।
  • পরিচ্ছন্নতা: প্রতি সারি এক-এক করে না ছেপে, পুরো সারিটা একটা string বানিয়ে একবারে ছাপলে code পরিষ্কার হয় আর print-call কমে।

মূল কথা ধীরগতি নয় — ভেতরের loop-এ কাজটা সরাসরি print না করে আগে জমিয়ে নেওয়া একটা ভালো অভ্যাস, যেটা পরে বড় output-এ কাজে দেয়।

9. Better thinking

ভেতরের loop-এ সরাসরি না ছেপে, প্রতি সারির সংখ্যাগুলো আগে একটা string-এ জমাই, তারপর সারিপিছু একবার করে print:

def print_triangle(n):
    for i in range(1, n + 1):
        row = " ".join(str(j) for j in range(1, i + 1))  # পুরো সারি একসাথে
        print(row)                                        # সারিপিছু একবার print

" ".join(...) সংখ্যাগুলোর মাঝে ফাঁকা বসিয়ে এক টুকরো string বানায় (যেমন "1 2 3"), তারপর সেটা একবারে ছাপি। print-call কমল (সারিপিছু একটা), trailing space-ও থাকে না — পরিষ্কার।

10. Thinking tweak

মূল মোচড় এক বাক্যে: বাইরের loop "কতগুলো সারি", ভেতরের loop "এই সারিতে কতদূর" — আর ভেতরের সীমা বাইরের চলক i-এর উপর নির্ভর করলেই ত্রিভুজ জন্ম নেয়।

এই নির্ভরতাই (ভেতরের loop range(1, i+1)) আসল জাদু:

  • ভেতরের loop যদি 1..i হয় → সোজা ত্রিভুজ (নিচে বাড়ে)
  • যদি i..n বা 1..(n-i+1) হয় → উল্টো ত্রিভুজ (নিচে কমে)
  • যদি j-এর বদলে i ছাপো → প্রতি সারিতে একই সংখ্যা (1, 2 2, 3 3 3...)

মানে কাঠামো এক — দুটো nested loop — শুধু ভেতরের loop-এর সীমা আর কী ছাপছি বদলেই হাজারটা pattern। মুখস্থ নয়, এই নির্ভরতাটা বোঝো।

11. Step-by-step dry run

n = 3-এ Section 9-এর version হাতে চালাই। বাইরের loop i-এর প্রতিটা মানে ভেতরে কী হয়, দেখো:

i (বাইরের) ভেতরের range(1, i+1) j গুলো row = " ".join print
1 range(1, 2) 1 "1" 1
2 range(1, 3) 1, 2 "1 2" 1 2
3 range(1, 4) 1, 2, 3 "1 2 3" 1 2 3

তিনটা সারি ছাপা শেষ, বাইরের loop থামল। চূড়ান্ত output:

1
1 2
1 2 3

এবার মনে মনে n = 0 চালাও: range(1, 1) খালি, বাইরের loop একবারও চলে না → কিছুই ছাপে না। ঠিক, n = 0-এ output খালি — আলাদা if লাগে না।

12. Python solution

def triangle_rows(n):
    """ত্রিভুজ pattern-টা list of string হিসেবে বানায় (print না করে)।
    এতে test করা সহজ — প্রতিটা সারি যাচাই করা যায়।"""
    rows = []
    for i in range(1, n + 1):
        row = " ".join(str(j) for j in range(1, i + 1))
        rows.append(row)
    return rows


def print_triangle(n):
    """ত্রিভুজটা সরাসরি ছাপে।"""
    for row in triangle_rows(n):
        print(row)


def same_number_rows(n):
    """variation: প্রতি সারিতে একই সংখ্যা (1 / 2 2 / 3 3 3 ...)।"""
    return [" ".join([str(i)] * i) for i in range(1, n + 1)]


# --- ছোট test গুলো (সব pass করা উচিত) ---
assert triangle_rows(4) == ["1", "1 2", "1 2 3", "1 2 3 4"]
assert triangle_rows(1) == ["1"]
assert triangle_rows(0) == []          # n = 0 -> খালি
assert triangle_rows(3) == ["1", "1 2", "1 2 3"]
assert same_number_rows(3) == ["1", "2 2", "3 3 3"]
assert same_number_rows(0) == []

# মোট সংখ্যা = n(n+1)/2 — সারিগুলোর শব্দ গুনে যাচাই
total = sum(len(r.split()) for r in triangle_rows(5))
assert total == 5 * 6 // 2            # 15

print("n = 4 ত্রিভুজ:")
print_triangle(4)
print("সব test pass!")

13. Line-by-line explanation

def triangle_rows(n):
    rows = []
    for i in range(1, n + 1):

প্রতিটা সারিকে আলাদা করে print না করে একটা list-এ জমাই — এতে পরে test-এ মিলিয়ে দেখা যায়। বাইরের loop i চলে 1 থেকে n পর্যন্ত (range(1, n+1) মানে n-ও ধরা)।

        row = " ".join(str(j) for j in range(1, i + 1))

এই problem-এর প্রাণ। ভেতরের অংশ range(1, i+1) দেয় 1 থেকে i পর্যন্ত সংখ্যা — খেয়াল করো সীমাটা i-এর উপর নির্ভর করছে, এজন্যই সারি যত নিচে তত লম্বা। str(j) প্রতিটাকে string বানায়, " ".join(...) মাঝে ফাঁকা বসিয়ে এক সারির string গাঁথে।

        rows.append(row)
    return rows

বানানো সারিটা list-এ জমা, সব শেষে list ফেরত।

def print_triangle(n):
    for row in triangle_rows(n):
        print(row)

জমানো সারিগুলো এক-এক করে ছাপি — সারিপিছু একটাই print, পরিষ্কার।

def same_number_rows(n):
    return [str(i)] * i ...

variation — এখানে j-এর বদলে i ছাপি, আর [str(i)] * i মানে "i লেখাটা i বার" (যেমন ["3","3","3"])। তাই 3 3 3। কাঠামো একই, শুধু কী ছাপছি বদলালো।

বাকি assert গুলো — সারির তালিকা, edge case (n = 0), আর মোট সংখ্যার সূত্র n(n+1)/2 — সব যাচাই করে; ঠিক থাকলে শেষে সব test pass!

14. Output walkthrough

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

n = 4 ত্রিভুজ:
1
1 2
1 2 3
1 2 3 4
সব test pass!

প্রথম লাইনটা শিরোনাম। তারপর print_triangle(4) চারটে সারি ছাপে — 1; 1 2; 1 2 3; 1 2 3 4 (i বাড়ার সাথে সারি লম্বা)। তার আগের সব assert (সারির তালিকা, n = 0 খালি, variation, যোগফল 15) চুপচাপ pass করেছে। শেষ লাইন সব test pass! — সব ঠিক।

15. Time complexity

O(n²) — বাইরের loop n বার, ভেতরের loop গড়ে n/2 বার, মোট কাজ 1 + 2 + ... + n = n(n+1)/2n²/2টা সংখ্যা ছাপা। n দ্বিগুণ হলে কাজ প্রায় চারগুণ। এটা pattern-এর স্বভাবগত — মোট এতগুলো সংখ্যা ছাপতে হলে এর কমে হয় না।

16. Space complexity

  • শুধু ছাপলে (print_triangle সরাসরি): O(n) — এক সময়ে একটা সারির string (সর্বোচ্চ n-টা সংখ্যা) মেমরিতে থাকে।
  • পুরো তালিকা ফেরালে (triangle_rows): O(n²) — সব সারি একসাথে রাখলে মোট n(n+1)/2টা সংখ্যার string জমে।

(শুধু console-এ ছাপা হলে অনেকে একে O(1) auxiliary-ও বলে, কারণ ছাপার পর কিছু রাখি না।)

17. Common mistakes

  1. range সীমায় off-by-onerange(1, n+1) লিখতে হয় n-কে ধরতে; range(1, n) লিখলে শেষ সারিটা বাদ পড়ে। আর ভেতরে range(1, i+1), range(1, i) নয়। এটাই এক নম্বর ভুল।
  2. print() দিয়ে newline ভুলে যাওয়া — ভেতরের loop শেষে সারি ভাঙতে newline দরকার; না দিলে সব সংখ্যা এক লাইনে লেপ্টে যায়। (string-জমানো version-এ print(row) নিজেই newline দেয়, তাই এ ভুল হয় না।)
  3. ভেতরের সীমা i-এর বদলে n দেওয়াrange(1, n+1) ভেতরে দিলে ত্রিভুজ না হয়ে আয়তক্ষেত্র (সব সারি সমান লম্বা) হয়ে যাবে। ভেতরের loop-কে i-এর উপর নির্ভর করতেই হবে।
  4. i আর j গুলিয়ে ফেলা — কোন সংখ্যা ছাপছি (j) আর কোন সারিতে আছি (i) — গুলিয়ে ফেললে pattern বদলে যায়।
  5. n = 0 / negative নিয়ে দুশ্চিন্তা — আলাদা if লাগে না; range(1, 0+1) = range(1, 1) খালি, loop এমনিই চলে না।

18. Harder problems that inherit this idea

  • HackerRank — Staircase (https://www.hackerrank.com/challenges/staircase/problem) — একই nested-loop ত্রিভুজ, শুধু সংখ্যার বদলে #, আর ডানে সারিবদ্ধ (আগে কিছু space, পরে star)।
  • LeetCode — Pascal's Triangle (https://leetcode.com/problems/pascals-triangle/) — ত্রিভুজ কাঠামো এক, কিন্তু প্রতি ঘরের মান উপরের দুই ঘরের যোগ — nested loop-এর উপর গণিত চাপানো।
  • LeetCode — Spiral Matrix (https://leetcode.com/problems/spiral-matrix/) — 2D grid-এ nested-loop চলাচলের আরও জটিল রূপ; এই "সারি-কলাম" চিন্তা সেখানেই ভিত্তি।

19. Pattern learned

Nested loops — বাইরের loop সারি, ভেতরের loop কলাম। বড় শিক্ষা: ভেতরের loop-এর সীমা বাইরের চলকের উপর নির্ভর করালেই (যেমন range(1, i+1)) ত্রিভুজ-জাতীয় আকৃতি তৈরি হয়; কাঠামো বদলায় না, শুধু সীমা আর "কী ছাপছি" বদলে নানা pattern। এই 2D চিন্তা পরে matrix, grid, 2D DP — সবখানে ফিরবে।

20. Final summary

ভবিষ্যতের আমাকে এক লাইনে: "pattern printing = nested loop; বাইরের i সারি (1..n), ভেতরের j কলাম (1..i — সীমা i-এর উপর নির্ভর বলেই ত্রিভুজ)। range(1, n+1) দিয়ে off-by-one এড়াও; সারি শেষে newline। কাঠামো এক, সীমা বদলেই সব pattern।"

এই note-টা digit-শাখার নয় — এটা nested loop শাখার শুরু। সম্পর্কিত পরের ধাপ Level 1-এ: factor/prime খোঁজার i*i <= n loop, আর তারও পরে sieve-এর nested loop।

ফিরে দেখা: এই 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" হিসেবে লেখা।