এই পর্বে যা শিখবেন
- গ্রিডে সবচেয়ে ছোট পথ গোনা, নিষিদ্ধ মোড় এড়ানো, আর DP দিয়ে একই উত্তর মেলানো
- Reflection principle-এর পূর্ণ প্রমাণ, আর তা থেকে Catalan সংখ্যা ও ballot problem
- Bijective proof আর double counting: দুটি শক্তিশালী প্রমাণ-কৌশল, উদাহরণসহ
- Complement, ক্ষেত্রভাগ আর recurrence দিয়ে গোনা, আর কোন প্রশ্নে কোন কৌশল তা বাছার ছক
- ইন্টারভিউ ধাঁচের গণনার ধাঁধা সমাধান, আর প্রতিটি উত্তর Python-এ brute force দিয়ে যাচাই
১ · গ্রিডে পথ — lattice path আর C(m + n, n)
কাল্পনিক অনলাইন দোকান বাজারঘরের একটি গুদাম শহরের এমন এলাকায়, যেখানে রাস্তাগুলো দাবার ছকের মতো: পূর্ব-পশ্চিমে কয়েকটি সমান্তরাল রাস্তা, উত্তর-দক্ষিণে কয়েকটি। একজন ডেলিভারি রাইডার গুদাম থেকে ৫ ব্লক পূর্বে আর ৩ ব্লক উত্তরে এক গ্রাহকের বাসায় যাবেন। সবচেয়ে ছোট পথে যেতে চাইলে তিনি কখনো পশ্চিমে বা দক্ষিণে যাবেন না; প্রতিটি মোড়ে শুধু পূর্বে (ডানে) বা উত্তরে (ওপরে)। প্রশ্ন: এমন আলাদা পথ কয়টি?
পুরান ঢাকার গলিতে রিকশাওয়ালাকে বললেন, “সবচেয়ে কাছের পথে চলুন।” তিনি ডানে-ওপরে কয়েকবার মোড় নেবেন, কিন্তু যেভাবেই যান, মোট ৫বার পূর্বে আর ৩বার উত্তরে যেতেই হবে। আলাদা পথ মানে শুধু কখন কোন দিকে মোড়, সেই ক্রমটি আলাদা। পথের পুরো গল্পটি তাই একটি ৮ অক্ষরের শব্দ, যেমন RRURRUUR, যেখানে R মানে ডানে আর U মানে ওপরে।
সমতলের যে বিন্দুর দুটি স্থানাঙ্কই পূর্ণসংখ্যা, তাকে বলে lattice point. (0, 0) থেকে (m, n) পর্যন্ত একটি lattice path হলো lattice point-এর একটি ক্রম, যার প্রতিটি ধাপ হয় R = (1, 0), নয়তো U = (0, 1). এখানে m, n ≥ 0 পূর্ণসংখ্যা।
(0, 0) থেকে (m, n) পর্যন্ত lattice path-এর সংখ্যা
C(m + n, n) = (m + n)! / (m! n!)
প্রমাণ (bijection দিয়ে):
- যেকোনো পথে x-স্থানাঙ্ক 0 থেকে m-এ ওঠে, আর প্রতিটি R ধাপ এটি ঠিক 1 বাড়ায়, U ধাপ বাড়ায় না। তাই পথে ঠিক m-টি R; একই যুক্তিতে ঠিক n-টি U, মোট m + n ধাপ। — স্থানাঙ্কের হিসাব
- প্রতিটি পথকে তার ধাপগুলোর শব্দে পাঠান: f(পথ) = R/U-এর দৈর্ঘ্য m + n-এর একটি শব্দ, যাতে m-টি R আর n-টি U. — f সুসংজ্ঞায়িত
- উল্টো দিকে, এমন যেকোনো শব্দ (0, 0) থেকে শুরু করে অক্ষর ধরে ধরে হাঁটলে ঠিক (m, n)-এ পৌঁছায়, আর পথটি শব্দ দিয়েই পুরো ঠিক হয়। তাই f-এর একটি inverse আছে, অর্থাৎ f একটি bijection (পর্ব ১৬)। — দুই দিকে inverse
- এমন শব্দ পুরো ঠিক হয় m + n-টি ঘরের কোন n-টিতে U বসল, তা দিয়ে; সেই বাছাই C(m + n, n) উপায়ে হয়। ∎ — combination-এর সংজ্ঞা
রাইডারের প্রশ্নে m = 5, n = 3, তাই পথ C(8, 3) = 56টি। এই প্রমাণটি একটি bijective proof: অচেনা বস্তু (পথ) গোনার বদলে আমরা একটি চেনা বস্তুর (R/U শব্দ) সঙ্গে এক-এক মিল দেখিয়েছি। পুরো পর্বে এই কৌশল বারবার ফিরবে।
দ্বিতীয় পথ: শেষ ধাপ ধরে ভাগ
N(x, y) দিয়ে (0, 0) থেকে (x, y)-এর পথের সংখ্যা বোঝান। যেকোনো পথ (x, y)-তে পৌঁছায় হয় বাঁ দিকের (x − 1, y) থেকে একটি R দিয়ে, নয়তো নিচের (x, y − 1) থেকে একটি U দিয়ে, আর এই দুই দল পরস্পর বিচ্ছিন্ন। যোগের নিয়মে (পর্ব ৩১ “গণনার মূল নিয়ম — যোগ, গুণ, বিয়োগ আর ভাগ”):
N(x, y) = N(x − 1, y) + N(x, y − 1), N(x, 0) = N(0, y) = 1
এটি আসলে Pascal-এর identity C(m + n, n) = C(m + n − 1, n) + C(m + n − 1, n − 1), শুধু গ্রিডে আঁকা (পর্ব ৩৪ “Binomial coefficient, Pascal-এর ত্রিভুজ আর combinatorial identity”-এ বিস্তারিত)। প্রোগ্রামিংয়ে এটিই গ্রিড-পথের dynamic programming, যার কোড আর জটিলতা DSA কোর্সের পর্ব ৫২-এ আছে। সূত্রের সুবিধা হলো এক লাইনে উত্তর; DP-র সুবিধা হলো বাধা থাকলেও কাজ করে। নিচের stepper ছোট একটি গ্রিডে টেবিলটি ভরে দেখায়।
1(0,0) → (3,2): 3 R, 2 U2কিনারা: N(x,0) = N(0,y) = 13y = 1: 2, 3, 44y = 2: 3, 6, 105C(5, 2) = 10 ✓6(1,1) বন্ধ: 10 − 2·3 = 4
| গ্রিড | |
| উত্তর |
- প্রশ্ন। (0, 0) থেকে (3, 2): প্রতিটি পথে ৩টি R আর ২টি U, মোট ৫ ধাপ। টেবিলের নিচের সারি y = 0, ওপরের সারি y = 2.
- কিনারা। নিচের সারিতে শুধু R দিয়েই যাওয়া যায়, বাঁ কলামে শুধু U দিয়ে; তাই সেখানে প্রতিটি ঘরে 1.
- সারি y = 1. প্রতিটি ঘর = বাঁয়ের ঘর + নিচের ঘর: N(1, 1) = 1 + 1 = 2, N(2, 1) = 2 + 1 = 3, N(3, 1) = 3 + 1 = 4.
- সারি y = 2. N(1, 2) = 1 + 2 = 3, N(2, 2) = 3 + 3 = 6, N(3, 2) = 6 + 4 = 10.
- মেলানো। সূত্র বলে C(3 + 2, 2) = C(5, 2) = 10; টেবিলের কোণের সংখ্যাও 10 ✓.
- বাধা। (1, 1) মোড় বন্ধ হলে সেই ঘরে 0 বসিয়ে আবার ভরুন: উত্তর 4. সূত্রে: (1, 1) দিয়ে যাওয়া পথ 2 × 3 = 6, আর 10 − 6 = 4 ✓.
নিষিদ্ধ বিন্দু এড়ানো — complement আর গুণের নিয়ম
বৃষ্টিতে রাইডারের পথের (2, 1) মোড়টি জলাবদ্ধ। কয়টি ছোট পথ এই মোড় এড়ায়? সরাসরি গোনার বদলে উল্টো দিক থেকে ভাবুন: আগে মোট পথ, তারপর বাদ দিন যেগুলো (2, 1) দিয়ে যায়। এটিই complement-এর নিয়ম. (2, 1) দিয়ে যাওয়া পথ দুই খণ্ডে ভাগ হয়: (0, 0) থেকে (2, 1), তারপর (2, 1) থেকে (5, 3). দুই খণ্ড স্বাধীনভাবে বাছা যায়, তাই গুণের নিয়মে C(3, 1) × C(5, 2) = 3 × 10 = 30. এড়ানো পথ 56 − 30 = 26.
গুদাম থেকে গ্রাহক: ৫ ব্লক পূর্বে, ৩ ব্লক উত্তরে
নীল রেখা একটি বৈধ ছোট পথ RRRUURRU; লাল ক্রস দেওয়া (2, 1) মোড়টি জলাবদ্ধ। ৫৬টি পথের ৩০টি এই মোড় দিয়ে যায়।
এবার Python-এ দুটি পথই মেলানো যাক: DP টেবিল (বাধার ঘরে 0) আর সূত্র। প্রথমবার চালালে ব্রাউজারে Python (Pyodide) নামতে কয়েক সেকেন্ড লাগে।
from math import comb
def count_paths(m, n, blocked=()):
"""DP: N(x, y) = N(x-1, y) + N(x, y-1); a blocked point gets 0"""
N = {}
for x in range(m + 1):
for y in range(n + 1):
if (x, y) in blocked:
N[x, y] = 0
elif x == 0 and y == 0:
N[x, y] = 1
else:
N[x, y] = N.get((x - 1, y), 0) + N.get((x, y - 1), 0)
return N
N = count_paths(3, 2)
for y in (2, 1, 0): # top row first, like the grid
print("y =", y, ":", [N[x, y] for x in range(4)])
print("paths to (3, 2):", N[3, 2], " C(5, 2) =", comb(5, 2))
# 5 blocks east, 3 north; the crossing (2, 1) is flooded
total = comb(8, 3)
through = comb(3, 1) * comb(5, 2) # (0,0)->(2,1), then (2,1)->(5,3)
dp = count_paths(5, 3, blocked={(2, 1)})[5, 3]
print("total:", total, " through (2, 1):", through)
print("avoid by formula:", total - through, " avoid by DP:", dp)
print("C(m+n, n) correct for every grid up to 8 x 8:",
all(count_paths(m, n)[m, n] == comb(m + n, n)
for m in range(9) for n in range(9)))আসল আউটপুট (ব্রাউজারের Python):
y = 2 : [1, 3, 6, 10]
y = 1 : [1, 2, 3, 4]
y = 0 : [1, 1, 1, 1]
paths to (3, 2): 10 C(5, 2) = 10
total: 56 through (2, 1): 30
avoid by formula: 26 avoid by DP: 26
C(m+n, n) correct for every grid up to 8 x 8: True
প্রথম তিন লাইন stepper-এর টেবিলটিই, ওপরের সারি আগে। এরপর বাজারঘরের প্রশ্ন: complement আর গুণের নিয়মে পাওয়া 26, আর বাধার ঘরে 0 বসানো DP-র 26, দুই আলাদা পদ্ধতি একই উত্তর দিয়েছে। শেষ লাইন ৮১টি গ্রিডে সূত্রটি পরীক্ষা করেছে; প্রমাণ সব m, n-এর জন্য, আর এই পরীক্ষা শুধু ভুল টাইপ বা off-by-one ধরার জাল। দুটি নিষিদ্ধ বিন্দু থাকলে একবার বাদ দিলেই হয় না, দুটি দিয়েই যাওয়া পথ ফেরত যোগ করতে হয়; সেটি অনুশীলনে আছে, আর পূর্ণ নিয়মটি পর্ব ৪৯ “Inclusion–exclusion আর derangement”-এর বিষয়।
২ · Reflection principle — খারাপ পথকে আয়নায় দেখা
এবার একটি কঠিন শর্ত। বাজারঘরের একটি ভ্যানে পার্সেল ওঠে আর নামে; ধরুন দিনে n-টি পার্সেল ওঠে আর n-টি নামে, আর নামানোর আগে সেটি উঠতে হবে, তাই কোনো মুহূর্তেই নামানো পার্সেলের সংখ্যা ওঠানোর চেয়ে বেশি হতে পারে না। ওঠা = R, নামা = U ধরলে প্রতিটি দিনের হিসাব (0, 0) থেকে (n, n)-এর একটি পথ, যা কখনো কর্ণ y = x-এর ওপরে যায় না (y ≤ x সবসময়)। এমন পথকে বলে Dyck path. কয়টি?
সব পথ C(2n, n)টি; সমস্যা হলো “খারাপ” পথগুলো গোনা, যারা কোনো এক সময় কর্ণের ওপরে ওঠে। খেয়াল করুন, কর্ণের ওপরে ওঠা মানে কোনো বিন্দুতে y = x + 1 রেখাটি ছোঁয়া, কারণ প্রতিটি ধাপে y − x ঠিক 1 বদলায়। খারাপ পথ সরাসরি গোনা কঠিন; কিন্তু তাদের একটি আয়নায় দেখলে ছবিটি হঠাৎ সহজ হয়ে যায়। এই কৌশলের নাম reflection principle.
n ≥ 1 হলে (0, 0) থেকে (n, n)-এর যে পথগুলো y = x + 1 রেখা অন্তত একবার ছোঁয়, তাদের সঙ্গে (0, 0) থেকে (n − 1, n + 1)-এর সব পথের একটি bijection আছে। ফলে খারাপ পথের সংখ্যা C(2n, n − 1).
প্রমাণ: খারাপ পথ P-তে প্রথম যে বিন্দুতে y = x + 1 ছোঁয়া হয়, তাকে T বলুন। φ(P) সংজ্ঞায়িত করুন: T পর্যন্ত পথ যেমন আছে তেমন, আর T-এর পরের প্রতিটি ধাপে R আর U অদলবদল।
- T-এর পরে P-তে বাকি ছিল কিছু R আর কিছু U, যা T = (t, t + 1) থেকে (n, n)-এ পৌঁছায়: n − t-টি R আর n − t − 1টি U. অদলবদলের পর সেখানে n − t − 1টি R আর n − t-টি U, তাই নতুন পথ শেষ হয় (t + n − t − 1, t + 1 + n − t) = (n − 1, n + 1)-এ। — ধাপ গোনা
- জ্যামিতিতে এটি T-এর পরের অংশকে রেখা y = x + 1-এর সাপেক্ষে প্রতিফলন; বিন্দু (n, n)-এর প্রতিফলন (n − 1, n + 1). — প্রতিফলন (x, y) ↦ (y − 1, x + 1)
- উল্টো দিকে, (0, 0) থেকে (n − 1, n + 1)-এর যেকোনো পথ Q শুরু করে রেখার নিচে (0 − 0 = 0 < 1) আর শেষ করে রেখার ওপরে (n + 1 − (n − 1) = 2 > 1); প্রতিটি ধাপে y − x ঠিক 1 বদলায়, তাই Q অবশ্যই কোথাও y − x = 1 ছোঁয়। প্রথম ছোঁয়ার পরে আবার R আর U অদলবদল করলে (n, n)-এর একটি খারাপ পথ পাওয়া যায়। — বিচ্ছিন্ন মধ্যবর্তী মান
- অদলবদল দুবার করলে পথ ফিরে আসে, আর প্রথম ছোঁয়ার বিন্দু T বদলায় না (T-এর আগের অংশ ছোঁয়া হয়নি)। তাই দুই ফাংশন পরস্পরের inverse, অর্থাৎ φ একটি bijection. — পর্ব ১৬
- (n − 1, n + 1)-এর পথ উপপাদ্য ১.২ অনুযায়ী C(2n, n + 1) = C(2n, n − 1)টি। ∎ — উপপাদ্য ১.২
প্রতিফলন: (3, 3)-এর খারাপ পথ থেকে (2, 4)-এর পথ
কমলা পথ RUURUR প্রথম বার লাল রেখা y = x + 1 ছোঁয় (1, 2)-এ; তার পরের অংশ উল্টে দিলে সবুজ পথ পৌঁছায় (2, 4)-এ।
n ≥ 0 হলে (0, 0) থেকে (n, n)-এর যে পথগুলো কখনো y = x-এর ওপরে যায় না, তাদের সংখ্যা
Cn = C(2n, n) − C(2n, n + 1) = C(2n, n) / (n + 1)
এই সংখ্যাগুলোকে বলে Catalan সংখ্যা: 1, 1, 2, 5, 14, 42, 132, 429, …
প্রমাণ: n = 0-তে একটিই (খালি) পথ, আর C(0, 0)/1 = 1. n ≥ 1 হলে:
- সব পথ C(2n, n)টি, আর প্রতিটি পথ হয় ভালো, নয়তো খারাপ (y = x + 1 ছোঁয়)। — উপপাদ্য ১.২, complement
- লেমা ২.১ অনুযায়ী খারাপ পথ C(2n, n + 1)টি, তাই ভালো পথ C(2n, n) − C(2n, n + 1). — বিয়োগ
- C(2n, n + 1) = (2n)! / ((n + 1)! (n − 1)!) = C(2n, n) · n/(n + 1). — factorial সাজানো
- তাই Cn = C(2n, n) (1 − n/(n + 1)) = C(2n, n)/(n + 1). ∎ — বীজগণিত
নামটি এসেছে বেলজিয়ান গণিতবিদ Eugène Catalan-এর নামে, যিনি ১৮৩৮ সালের দিকে বন্ধনী বসানোর উপায় নিয়ে লেখেন; তবে সংখ্যাগুলো তার আগেই Euler-এর হাতে (বহুভুজের triangulation, আনুমানিক ১৭৫১) এসেছিল, আর ভাবনার প্রশ্নে এর ইতিহাস আছে। Catalan সংখ্যার recurrence, generating function আর আরও প্রয়োগ পর্ব ৫০ “বিশেষ সংখ্যা — Catalan, Stirling আর partition”-এ আসবে; এখানে মূল কথা হলো প্রমাণটি সম্পূর্ণ গণনামূলক। নিচের stepper n = 3-এ প্রতিফলনটি ধাপে ধাপে দেখায়।
1পথ: R U U R U R → (3,3)2(1,0) (1,1) (1,2) ← ছোঁয়া3বাকি R U R → U R U4নতুন: R U U U R U → (2,4)5উল্টো: আবার অদলবদল620 − 15 = 5 = C₃
| পথ | |
| শেষ বিন্দু | |
| গোনা |
- পথ। RUURUR-এ ৩টি R আর ৩টি U, তাই এটি (0, 0) থেকে (3, 3)-এর একটি পথ। এটি কি কর্ণের ওপরে যায়?
- প্রথম ছোঁয়া। বিন্দুগুলো (1, 0), (1, 1), (1, 2): তৃতীয় ধাপে y − x = 1, অর্থাৎ পথটি y = x + 1 ছুঁয়েছে। তাই এটি খারাপ, আর T = (1, 2).
- প্রতিফলন। T-এর পরের তিন ধাপ R U R-এ R আর U অদলবদল করুন: হয়ে যায় U R U. T পর্যন্ত অংশ (RUU) বদলায় না।
- নতুন শেষ বিন্দু। নতুন পথ RUUURU-এ ২টি R আর ৪টি U, তাই এটি শেষ হয় (2, 4)-এ, যা (3, 3)-এর প্রতিফলন (n − 1, n + 1).
- উল্টো পথ। RUUURU-ও প্রথম বার (1, 2)-এ রেখা ছোঁয়; সেখান থেকে আবার অদলবদল করলে মূল RUURUR ফিরে আসে। তাই মিলটি এক-এক।
- গোনা। সব পথ C(6, 3) = 20, খারাপ পথ = (2, 4)-এর পথ = C(6, 2) = 15, ভালো পথ 20 − 15 = 5 = C(6, 3)/4 = C₃ ✓.
Ballot problem — সবসময় এগিয়ে থাকা
একটি নির্বাচনে প্রার্থী A পেয়েছেন a ভোট, B পেয়েছেন b ভোট, a > b. ভোটগুলো একটি একটি করে গোনা হয়। কতগুলো গোনার ক্রমে A প্রতিটি ভোটের পর কঠোরভাবে এগিয়ে থাকেন? এই প্রশ্নের নাম ballot problem.
a > b ≥ 0 পূর্ণসংখ্যা হলে a-টি A আর b-টি B-এর এমন সারি, যার প্রতিটি শুরুর অংশে A-এর সংখ্যা B-এর চেয়ে বেশি, তাদের সংখ্যা
C(a + b − 1, b) − C(a + b − 1, b − 1) = ((a − b)/(a + b)) · C(a + b, a)
(b = 0 হলে দ্বিতীয় পদ 0 ধরুন।) তাই সব ক্রম সমান সম্ভাব্য হলে A সারাক্ষণ এগিয়ে থাকার সম্ভাবনা (a − b)/(a + b).
প্রমাণ: A = R, B = U ধরুন; সারিটি (0, 0) থেকে (a, b)-এর পথ, আর “A এগিয়ে” মানে প্রতিটি বিন্দুতে x > y.
- প্রথম ভোটটি A-এর হতেই হবে, তাই ভালো পথগুলো (1, 0) থেকে (a, b)-এর এমন পথ, যারা কখনো y = x রেখা ছোঁয় না। (1, 0) থেকে সব পথ C(a + b − 1, b)টি। — উপপাদ্য ১.২
- (1, 0) থেকে যে পথ y = x ছোঁয়, তার প্রথম ছোঁয়ার আগের অংশ y = x-এর সাপেক্ষে প্রতিফলিত করুন (R ↔ U অদলবদল)। এতে শুরু হয় (0, 1) থেকে, আর এভাবে এমন পথগুলো (0, 1) থেকে (a, b)-এর সব পথের সঙ্গে এক-এক মেলে। — প্রতিফলন
- (0, 1) থেকে (a, b)-এর প্রতিটি পথ অবশ্যই y = x ছোঁয়, কারণ শুরুতে y > x আর শেষে x > y; এদের সংখ্যা C(a + b − 1, b − 1). — মধ্যবর্তী মান, উপপাদ্য ১.২
- বিয়োগ করে প্রথম রূপ; বীজগণিতে সরল করলে ((a − b)/(a + b)) · C(a + b, a). ∎ — complement
উদাহরণ: a = 5, b = 3 হলে C(7, 3) − C(7, 2) = 35 − 21 = 14, আর সূত্রে (2/8) · 56 = 14. খেয়াল করুন, a = n + 1, b = n বসালে উত্তর Cn হয়: ballot আর Catalan একই পরিবারের সদস্য। নিচের ব্লক n = 4-এ প্রতিফলনটি সব খারাপ পথে চালিয়ে দেখে, আর ballot-এর ক্রমগুলো সরাসরি গোনে।
from itertools import combinations
from math import comb
def walk(steps): # R = one step right, U = one step up
x, y, pts = 0, 0, []
for s in steps:
x, y = (x + 1, y) if s == "R" else (x, y + 1)
pts.append((x, y))
return pts
def reflect(steps): # swap R and U after the first touch of y = x + 1
for i, (x, y) in enumerate(walk(steps)):
if y == x + 1:
tail = "".join("U" if s == "R" else "R" for s in steps[i + 1:])
return steps[:i + 1] + tail
return None # a good path never touches the line
n = 4
paths = ["".join("U" if i in ups else "R" for i in range(2 * n))
for ups in combinations(range(2 * n), n)]
bad = [p for p in paths if reflect(p) is not None]
images = {reflect(p) for p in bad}
print("all:", len(paths), " good:", len(paths) - len(bad), " bad:", len(bad))
print("distinct images:", len(images), " all end at (3, 5):",
all(walk(q)[-1] == (n - 1, n + 1) for q in images))
print("C(8, 3) =", comb(8, 3), " C(8, 4)/5 =", comb(8, 4) // 5)
print("example:", "RUURRUUR", "->", reflect("RUURRUUR"))
a, b = 5, 3 # ballot: A must lead after every vote
seqs = ["".join("B" if i in bs else "A" for i in range(a + b))
for bs in combinations(range(a + b), b)]
lead = [s for s in seqs if all(s[:k].count("A") > s[:k].count("B")
for k in range(1, a + b + 1))]
print("ballot:", len(lead), "of", len(seqs),
" formula:", (a - b) * comb(a + b, a) // (a + b))আসল আউটপুট (ব্রাউজারের Python):
all: 70 good: 14 bad: 56
distinct images: 56 all end at (3, 5): True
C(8, 3) = 56 C(8, 4)/5 = 14
example: RUURRUUR -> RUUUURRU
ballot: 14 of 56 formula: 14
৭০টি পথের ৫৬টি খারাপ। তাদের প্রতিফলন ৫৬টি আলাদা পথ দেয় (তাই φ one-to-one), আর প্রতিটি শেষ হয় (3, 5)-এ; যেহেতু (3, 5)-এর পথও ঠিক C(8, 3) = 56টি, φ onto-ও। ভালো পথ 14 = C(8, 4)/5 = C₄. উদাহরণে RUURRUUR তৃতীয় ধাপে (1, 2)-এ রেখা ছোঁয়, বাকি RRUUR উল্টে UURRU হয়। শেষ লাইনে ৫৬টি ভোট-গোনার ক্রমের মধ্যে ১৪টিতে A সারাক্ষণ এগিয়ে, অর্থাৎ সম্ভাবনা 14/56 = 1/4 = (5 − 3)/(5 + 3) ✓.
৩ · কম্পিউটার বিজ্ঞানে Catalan — বন্ধনী, binary tree আর triangulation
Catalan সংখ্যার আসল জাদু হলো এটি একেবারে আলাদা চেহারার অনেক প্রশ্নের উত্তর। n = 3-এ পাঁচটি করে বস্তু নিচের সারণিতে; প্রতিটি সারিতে সংখ্যা 5 = C₃. আর এই মিলগুলো কাকতালীয় নয়: প্রতিটির পেছনে একটি bijection আছে।
| বস্তু (আকার n) | n = 3-এর উদাহরণ | মিলের সূত্র |
|---|---|---|
| কর্ণ না পেরোনো পথ (Dyck path) | RRRUUU, RRURUU, RRUURU, RURRUU, RURURU | মূল সংজ্ঞা |
| n জোড়া মেলানো বন্ধনী | ((())), (()()), (())(), ()(()), ()()() | R = “(”, U = “)” |
| n node-এর binary tree | ৫টি আকার | প্রথম ফেরার জায়গায় ভাঙা |
| (n + 2)-ভুজের triangulation | পঞ্চভুজে ৫টি | একটি বাহুর ত্রিভুজ ধরে ভাঙা |
| Stack-এ n push আর n pop-এর বৈধ ক্রম | push = R, pop = U | খালি stack থেকে pop নিষেধ |
বন্ধনী: “(” = R আর “)” = U পড়লে একটি বন্ধনী-সারি মেলানো হয় ঠিক তখন, যখন কোনো শুরুর অংশে “)” বেশি নেই, আর শেষে সমান সমান; এটি হুবহু Dyck path-এর শর্ত। পর্ব ১২-এ balanced parentheses-এর recursive সংজ্ঞা থেকে দৈর্ঘ্য 0, 2, 4, 6, 8-এ 1, 1, 2, 5, 14টি সারি পাওয়া গিয়েছিল; এখন তার সূত্র আর প্রমাণ দুটোই আপনার হাতে।
Catalan recurrence: একটি মেলানো সারি (n + 1 জোড়া) সবসময় “(” দিয়ে শুরু হয়; তার জোড়া “)” কোথায়, তা দিয়ে সারিটি অনন্যভাবে ভাঙে: ( A ) B, যেখানে A-তে i জোড়া আর B-তে n − i জোড়া, দুটিই মেলানো। i = 0, …, n-এর জন্য দুই অংশ স্বাধীন, তাই যোগ আর গুণের নিয়মে
Cn+1 = C0Cn + C1Cn−1 + … + CnC0, C0 = 1
যেমন C₃ = C₀C₂ + C₁C₁ + C₂C₀ = 2 + 1 + 2 = 5. এই recurrence-এর পূর্ণ প্রমাণ অনুশীলনের প্রথম প্রশ্ন।
Binary tree: n node-এর একটি binary tree-এ root বাদে বাকি n − 1টি node বাঁ subtree-তে i-টি আর ডান subtree-তে n − 1 − i-টি ভাগ হয়; গোনাটি একই recurrence মানে, তাই সংখ্যা Cn. বন্ধনীর সঙ্গে সরাসরি মিল: tree T-কে লিখুন “( বাঁ subtree-র সারি ) ডান subtree-র সারি”. DSA কোর্সের পর্ব ২৬ আর পর্ব ২৭-এ এই সংখ্যাই আসে n চাবির সম্ভাব্য BST-এর আকার হিসেবে।
Triangulation: উত্তল (n + 2)-ভুজকে পরস্পর না কাটা কর্ণ দিয়ে ত্রিভুজে ভাগ করার উপায়কে বলে triangulation. একটি নির্দিষ্ট বাহু ধরুন; সেটি ঠিক একটি ত্রিভুজে থাকে, আর সেই ত্রিভুজের তৃতীয় শীর্ষবিন্দু বহুভুজটিকে দুটি ছোট বহুভুজে ভাগ করে। আবার একই recurrence. তাই ষড়ভুজে (n = 4) triangulation 14টি। Matrix chain multiplication-এ n-টি matrix-এর বন্ধনী বসানোর উপায়ও Cn−1, যার কারণে সব উপায় চেষ্টা করা অসম্ভব আর DP লাগে (DSA কোর্সের পর্ব ৫৪)।
৪ · Bijective proof — subset, bit string আর composition
এ পর্যন্ত তিনবার একই কৌশল দেখলেন: পথ ↔ R/U শব্দ, খারাপ পথ ↔ অন্য বিন্দুর পথ, বন্ধনী ↔ Dyck path. মূল নীতিটি সহজ: দুটি সসীম সেটের মধ্যে একটি bijection থাকলে তাদের আকার সমান। এর শক্তি হলো, কঠিন সেটটি গোনার দরকারই পড়ে না।
Subset ↔ bit string
S = {1, 2, …, n}-এর প্রতিটি subset A-কে একটি n-দৈর্ঘ্যের bit string-এ পাঠান: i-তম bit 1 যদি i ∈ A, নইলে 0. যেমন n = 4-এ {1, 3} ↦ 1010. উল্টো দিকে প্রতিটি bit string একটি subset ঠিক করে, তাই এটি bijection, আর bit string 2nটি বলে subset-ও 2nটি। এটি পর্ব ১৪-এর |P(S)| = 2n-এর আরেকটি প্রমাণ, আর প্রোগ্রামিংয়ে bitmask দিয়ে subset ঘোরানোর ভিত্তি।
Composition গোনা
n-এর একটি composition হলো ধনাত্মক পূর্ণসংখ্যার একটি ক্রমসহ তালিকা, যার যোগফল n. যেমন 4-এর composition: 4, 3 + 1, 1 + 3, 2 + 2, 2 + 1 + 1, 1 + 2 + 1, 1 + 1 + 2, 1 + 1 + 1 + 1, মোট ৮টি। বাজারঘরের ভাষায়: ৪ দিনের একটি ডেলিভারি সূচি কয়টি ধাপে ভাগ করা যায়, যেখানে প্রতিটি ধাপ অন্তত এক দিনের আর ধাপগুলোর ক্রম গুরুত্বপূর্ণ।
n ≥ 1 হলে n-এর composition-এর সংখ্যা 2n−1, আর ঠিক k অংশের composition-এর সংখ্যা C(n − 1, k − 1).
প্রমাণ (bijection দিয়ে): n-টি তারা এক সারিতে রাখুন: ★ ★ ★ ★. পরপর দুই তারার মাঝে n − 1টি ফাঁক, যাদের নাম দিন 1, 2, …, n − 1.
- f: ফাঁকের যেকোনো subset K ⊆ {1, …, n − 1} নিন, আর K-এর প্রতিটি ফাঁকে একটি দেয়াল বসান। দেয়ালগুলো সারিটিকে |K| + 1টি খণ্ডে ভাগ করে, প্রতিটিতে অন্তত একটি তারা (দুই দেয়াল একই ফাঁকে বসে না)। খণ্ডগুলোর তারার সংখ্যা বাঁ থেকে ডানে একটি composition. — f সুসংজ্ঞায়িত
- g: composition (c1, …, ck)-এর চলমান যোগফল c1, c1 + c2, …, c1 + … + ck−1 নিন; এরা {1, …, n − 1}-এর k − 1টি আলাদা সংখ্যা, কারণ প্রতিটি cᵢ ≥ 1. — g সুসংজ্ঞায়িত
- দেয়ালের অবস্থান ঠিক চলমান যোগফল, তাই g(f(K)) = K আর f(g(c)) = c; f একটি bijection. — দুই দিকে inverse
- {1, …, n − 1}-এর subset 2n−1টি, আর k − 1 আকারের subset C(n − 1, k − 1)টি। ∎ — পর্ব ১৪, combination
দ্বিতীয় অংশটি পর্ব ৩৫ “পুনরাবৃত্তিসহ বাছাই — stars and bars আর multinomial”-এর x1 + … + xk = n, xᵢ ≥ 1-এর C(n − 1, k − 1)টি সমাধান; সেখানে যে bijective প্রমাণের প্রতিশ্রুতি ছিল, এটিই সেই প্রমাণ। নিচের ব্লক n = 4-এর সব ৮টি মিল ছাপে, আর n = 12 পর্যন্ত bijection-টি পরীক্ষা করে।
from itertools import combinations
def comp_from_cuts(n, cuts):
"""cut the row of n stars after each position in cuts (a subset of 1..n-1)"""
parts, last = [], 0
for c in sorted(cuts) + [n]:
parts.append(c - last)
last = c
return parts
def cuts_from_comp(parts): # inverse: the running sums, except the last
sums, s = set(), 0
for p in parts[:-1]:
s += p
sums.add(s)
return sums
n = 4
for k in range(n):
for cuts in combinations(range(1, n), k):
bits = "".join("1" if i in cuts else "0" for i in range(1, n))
parts = comp_from_cuts(n, list(cuts))
print(bits, set(cuts) or "{}", "->", " + ".join(map(str, parts)))
ok = True
for n in range(1, 13):
seen = set()
for k in range(n):
for cuts in combinations(range(1, n), k):
parts = comp_from_cuts(n, list(cuts))
ok = ok and sum(parts) == n and cuts_from_comp(parts) == set(cuts)
seen.add(tuple(parts))
ok = ok and len(seen) == 2 ** (n - 1)
print("bijection and 2^(n-1) compositions for n = 1..12:", ok)আসল আউটপুট (ব্রাউজারের Python):
000 {} -> 4
100 {1} -> 1 + 3
010 {2} -> 2 + 2
001 {3} -> 3 + 1
110 {1, 2} -> 1 + 1 + 2
101 {1, 3} -> 1 + 2 + 1
011 {2, 3} -> 2 + 1 + 1
111 {1, 2, 3} -> 1 + 1 + 1 + 1
bijection and 2^(n-1) compositions for n = 1..12: True
প্রতিটি লাইনে তিনটি রূপ পাশাপাশি: ফাঁকের bit string, দেয়ালের subset, আর composition. ফাঁকা subset মানে কোনো দেয়াল নেই, তাই composition শুধু 4; পূর্ণ subset {1, 2, 3} মানে প্রতিটি ফাঁকে দেয়াল, তাই 1 + 1 + 1 + 1. এখানে আসলে দুটি bijection একসঙ্গে: composition ↔ subset ↔ bit string. শেষ লাইন দেখায় যে n = 1 থেকে 12 পর্যন্ত মোট ৪০৯৫টি composition-এর প্রতিটিতে উল্টো ফাংশন ঠিক কাজ করেছে আর প্রতিটি n-এ সংখ্যা 2n−1.
৫ · Double counting — একই জিনিস দুইভাবে গোনা
পরীক্ষার হলে শিক্ষক উপস্থিতি মেলান দুইভাবে: প্রতিটি বেঞ্চে কয়জন বসেছে যোগ করে, আর প্রতিটি সারির খাতায় কয়টি সই আছে যোগ করে। দুই যোগফল একই হতেই হবে, কারণ দুটিই একই মানুষদের গোনে। এই সাধারণ বুদ্ধিই double counting: একটি সেটকে দুইভাবে গুনুন, তারপর দুই রাশি সমান বসান। প্রায়ই এক দিকের রাশিটি জানা, আর অন্য দিকে একটি নতুন সমতা বা অসম্ভবতার প্রমাণ বেরিয়ে আসে।
A আর B সসীম সেট, আর R ⊆ A × B একটি সম্পর্ক (কোন a কোন b-এর সঙ্গে যুক্ত)। r(a) = a যতগুলো b-এর সঙ্গে যুক্ত, আর c(b) = b যতগুলো a-এর সঙ্গে যুক্ত। তাহলে ∑a∈A r(a) = |R| = ∑b∈B c(b).
প্রমাণ:
- একটি টেবিল আঁকুন যার সারি A-এর উপাদান, কলাম B-এর; (a, b) ∈ R হলে ঘরে 1, নইলে 0. এটিকে বলে incidence matrix. টেবিলের সব 1-এর সংখ্যা |R|. — সংজ্ঞা
- সারি ধরে যোগ: a-এর সারিতে 1 আছে r(a)টি, আর সারিগুলো R-কে পরস্পর বিচ্ছিন্ন অংশে ভাগ করে; তাই সব সারির যোগ |R|. — যোগের নিয়ম
- কলাম ধরে যোগেও একই যুক্তি: সব কলামের যোগ |R|. দুটি সমান বসালে উপপাদ্য। ∎ — যোগের নিয়ম
1 ঢাকা খুলনা রাজশাহী2R1 1 1 0 │ 23R2 0 1 1 │ 24R3 1 1 1 │ 35R4 1 0 0 │ 16 3 3 2
| সারির যোগ | |
| কলামের যোগ |
- টেবিল। ৪ জন রাইডার (R1–R4) আর ৩টি জেলা। কোনো রাইডার কোনো জেলায় অন্তত একটি ডেলিভারি দিলে ঘরে 1.
- সারি ধরে। প্রতিটি রাইডার কয়টি জেলায় গেছেন: R1 → 2, R2 → 2, R3 → 3, R4 → 1.
- সারির মোট। 2 + 2 + 3 + 1 = 8. এটি (রাইডার, জেলা) জোড়ার সংখ্যা, অর্থাৎ টেবিলের সব 1.
- কলাম ধরে। প্রতিটি জেলায় কয়জন রাইডার গেছেন: ঢাকা 3, খুলনা 3, রাজশাহী 2.
- দুই দিক সমান। 3 + 3 + 2 = 8. একই ৮টি জোড়া দুইভাবে গোনা হয়েছে; একটি হিসাব না মিললে টেবিলে ভুল আছে।
প্রয়োগ ১: Handshake — degree-র যোগফল জোড়
একটি অনুষ্ঠানে কয়েকজন মানুষ কয়েকবার করমর্দন করলেন। প্রত্যেকে কয়বার করমর্দন করেছেন, তার যোগফল সবসময় জোড়, কারণ প্রতিটি করমর্দনে ঠিক দুজন থাকেন। টেবিলের ভাষায়: সারি = মানুষ, কলাম = করমর্দন, প্রতিটি কলামে ঠিক দুটি 1. কলাম ধরে যোগ = 2 × (করমর্দনের সংখ্যা), সারি ধরে যোগ = প্রত্যেকের সংখ্যার যোগফল। গ্রাফের ভাষায় এটি handshake lemma: সব vertex-এর degree-র যোগফল = 2|E|, ফলে বিজোড় degree-র vertex সবসময় জোড় সংখ্যক। গ্রাফ নিয়ে পূর্ণ আলোচনা পর্ব ৫৭ “গ্রাফ — সংজ্ঞা, degree আর handshake theorem”-এ; এখানে লক্ষ করুন, প্রমাণটি পুরোপুরি double counting.
একটি তাৎক্ষণিক ফল: ৯ জনের একটি দলে প্রত্যেকে ঠিক ৫ জনের সঙ্গে করমর্দন করবেন, এমন ব্যবস্থা অসম্ভব, কারণ যোগফল 9 × 5 = 45 বিজোড়। আর ১০ জনের প্রত্যেকে বাকি সবার সঙ্গে একবার করে করমর্দন করলে সারি ধরে 10 × 9 = 90, তাই করমর্দন 90/2 = 45 = C(10, 2).
প্রয়োগ ২: অধিনায়কসহ দল — k · C(n, k) = n · C(n − 1, k − 1)
n জন খেলোয়াড় থেকে k জনের একটি দল বানাবেন, আর দলের ভেতর থেকে একজন অধিনায়ক। (দল, অধিনায়ক) জোড়া দুইভাবে গোনা যায়।
- আগে দল, তারপর অধিনায়ক: দল C(n, k) উপায়ে, প্রতিটি দলে অধিনায়ক k উপায়ে; গুণের নিয়মে k · C(n, k). — প্রথম গোনা
- আগে অধিনায়ক, তারপর বাকি দল: অধিনায়ক n উপায়ে, বাকি n − 1 জন থেকে k − 1 জন C(n − 1, k − 1) উপায়ে; মোট n · C(n − 1, k − 1). — দ্বিতীয় গোনা
- দুটিই একই সেটের আকার, তাই সমান। ∎ — double counting
উদাহরণ: ৬ জন থেকে ৪ জনের দল, অধিনায়কসহ: 4 × 15 = 60 আর 6 × 10 = 60. বীজগণিতে factorial খুলেও প্রমাণ করা যায়, কিন্তু double counting প্রমাণটি বলে কেন সমতাটি সত্য। এই সমতা k দিয়ে যোগ করলে পর্ব ৩৪-এর ∑ k · C(n, k) = n · 2n−1 আসে। এ ধরনের যুক্তিকে সাধারণভাবে বলে combinatorial proof.
৬ · Complement, ক্ষেত্রভাগ আর recurrence দিয়ে গোনা
বাজারঘর ডেলিভারির সময়সূচি bit string-এ রাখে: 1 মানে সেদিন রাইডার কাজে, 0 মানে ছুটি। শ্রম-নীতি অনুযায়ী (কাল্পনিক নিয়ম) পরপর দুই দিন ছুটি দেওয়া যাবে না, অর্থাৎ string-এ “00” থাকবে না। n দিনের কতগুলো বৈধ সূচি আছে? সরাসরি সূত্র চোখে পড়ে না; কিন্তু ছোট ক্ষেত্র ভাগ করে ভাবলে একটি recurrence বেরিয়ে আসে।
an = দৈর্ঘ্য n-এর এমন bit string-এর সংখ্যা, যাতে “00” নেই। তাহলে a1 = 2, a2 = 3, আর n ≥ 3 হলে an = an−1 + an−2. ফলে an = Fn+2, যেখানে F0 = 0, F1 = 1 দিয়ে শুরু Fibonacci ধারা।
প্রমাণ:
- শুরুর মান: দৈর্ঘ্য 1-এ 0, 1 দুটিই বৈধ; দৈর্ঘ্য 2-এ 01, 10, 11 বৈধ, 00 নয়। — তালিকা
- n ≥ 3 হলে বৈধ string-গুলোকে শেষ bit অনুযায়ী দুই পরস্পর বিচ্ছিন্ন দলে ভাগ করুন। শেষ bit 1: আগের n − 1 bit যেকোনো বৈধ string, আর শেষে 1 যোগ করলে নতুন “00” তৈরি হয় না; তাই an−1টি। — ক্ষেত্র ১
- শেষ bit 0: তার আগের bit অবশ্যই 1 (নইলে শেষে “00”), তাই string-টি “…10”, আর আগের n − 2 bit যেকোনো বৈধ string; তাই an−2টি। — ক্ষেত্র ২
- যোগের নিয়মে an = an−1 + an−2. — যোগের নিয়ম
- F3 = 2 = a1, F4 = 3 = a2, আর দুই ধারা একই recurrence মানে; পর্ব ১১-এর strong induction-এ সব n-এ an = Fn+2. ∎ — strong induction
“অন্তত একবার পরপর দুই দিন ছুটি” আছে এমন string গোনার প্রশ্ন এলে সরাসরি গোনা কঠিন (কোথায় প্রথম “00”, কয়বার, ওভারল্যাপ…)। Complement-এ এক লাইনে উত্তর: 2n − an. এটিই “অন্তত একটি” প্রশ্নের সোনালি নিয়ম। Recurrence বানানো আর সমাধান করার পূর্ণ কৌশল পর্ব ৪৪ “Recurrence দিয়ে মডেল — Fibonacci, Hanoi আর bit string” থেকে শুরু হবে; আর পর্ব ১২-এ Fibonacci-র বৃদ্ধির হার দেখেছেন। নিচের ব্লক brute force, recurrence আর Fibonacci তিনটি কলাম পাশাপাশি রাখে।
from itertools import product
def no00(n): # brute force over all 2^n strings
return sum(1 for s in product("01", repeat=n) if "00" not in "".join(s))
def split(n): # cases: ends in 1, or ends in 10
good = ["".join(s) for s in product("01", repeat=n)
if "00" not in "".join(s)]
return (sum(s.endswith("1") for s in good),
sum(s.endswith("10") for s in good))
a = {1: 2, 2: 3} # a(n) = a(n-1) + a(n-2)
for n in range(3, 13):
a[n] = a[n - 1] + a[n - 2]
fib = [0, 1]
while len(fib) < 16:
fib.append(fib[-1] + fib[-2])
print(" n brute recur F(n+2) with 00")
for n in range(1, 13):
b = no00(n)
print(f"{n:2} {b:6} {a[n]:6} {fib[n + 2]:7} {2 ** n - b:8}")
print("n = 6 split (ends 1, ends 10):", split(6), " a(5), a(4) =", a[5], a[4])আসল আউটপুট (ব্রাউজারের Python):
n brute recur F(n+2) with 00
1 2 2 2 0
2 3 3 3 1
3 5 5 5 3
4 8 8 8 8
5 13 13 13 19
6 21 21 21 43
7 34 34 34 94
8 55 55 55 201
9 89 89 89 423
10 144 144 144 880
11 233 233 233 1815
12 377 377 377 3719
n = 6 split (ends 1, ends 10): (13, 8) a(5), a(4) = 13 8
তিনটি কলাম প্রতিটি সারিতে মেলে: 212 = 4096টি string-এর brute force, দুই লাইনের recurrence, আর Fibonacci. শেষ কলাম complement: দৈর্ঘ্য 8-এ 256 − 55 = 201টি string-এ অন্তত একটি “00”, আর n বাড়ার সঙ্গে বৈধ string-এর অনুপাত দ্রুত কমে। শেষ লাইন প্রমাণের ক্ষেত্রভাগটি সরাসরি দেখায়: দৈর্ঘ্য 6-এর ২১টি বৈধ string-এর ১৩টি 1-এ শেষ, যা a5-এর সমান, আর ৮টি 10-এ শেষ, যা a4-এর সমান। Brute force এখানে 2nটি string দেখে, তাই n = 40-এই তা এক লাখ কোটির বেশি; recurrence সেখানে ৪০টি যোগেই শেষ।
৭ · কোন কৌশল কখন — সিদ্ধান্তের ছক আর ইন্টারভিউ ধাঁধা
গণনার প্রশ্নে সবচেয়ে কঠিন ধাপ প্রায়ই সূত্র নয়, কৌশল বাছাই। প্রশ্নের ভাষায় কিছু সংকেত থাকে; নিচের ছকটি সেগুলো এক জায়গায় আনে।
| প্রশ্নে যে সংকেত | কৌশল | উদাহরণ |
|---|---|---|
| পরপর স্বাধীন পছন্দ | গুণের নিয়ম | ৬ অক্ষরের কুপন কোড |
| “অন্তত একটি”, “কোনো একটি নয়” | Complement | অন্তত একটি “00” আছে |
| শুধু ডানে/ওপরে, “কখনো পেরোবে না” | Lattice path, reflection | Dyck path, ballot |
| অচেনা বস্তু, কিন্তু চেনা কিছুর মতো দেখতে | Bijection | composition ↔ subset |
| দুই ধরনের বস্তুর মধ্যে সম্পর্ক, বা জোড়-বিজোড়ের প্রশ্ন | Double counting | handshake, অধিনায়কসহ দল |
| শেষ (বা প্রথম) ধাপের কয়েকটি সম্ভাবনা | ক্ষেত্রভাগ → recurrence | “00” ছাড়া string, সিঁড়ি |
| “প্রতিটি কি অন্তত…?”, অস্তিত্বের প্রশ্ন | Pigeonhole | একই জন্মমাস |
| একাধিক বাধা বা শর্ত একসঙ্গে | Inclusion–exclusion | দুটি নিষিদ্ধ মোড় |
Pigeonhole-এর সারির জন্য পর্ব ৩২ দেখুন; শেষ সারিটি পর্ব ৪৯-এ আসবে। এবার কয়েকটি ইন্টারভিউ ধাঁচের ধাঁধা, প্রতিটির উত্তর scratch-এ brute force দিয়ে মেলানো।
ধাঁধা ১: ৪ × 3 গ্রিডে কয়টি আয়তক্ষেত্র?
৪ ঘর চওড়া আর ৩ ঘর উঁচু একটি গ্রিড (যেমন একটি তাক)। এর রেখা ধরে আঁকা যায় এমন সব আয়তক্ষেত্র (বর্গসহ) কয়টি? Bijection: প্রতিটি আয়তক্ষেত্র ঠিক হয় দুটি উল্লম্ব রেখা আর দুটি আনুভূমিক রেখা দিয়ে। উল্লম্ব রেখা ৫টি, আনুভূমিক ৪টি, তাই C(5, 2) × C(4, 2) = 10 × 6 = 60.
ধাঁধা ২: ১০ ধাপের সিঁড়ি, একবারে ১ বা ২ ধাপ
উপায়গুলো ১ আর ২ দিয়ে 10-এর composition. শেষ পদক্ষেপ ১ হলে বাকি 9 ধাপ, ২ হলে বাকি 8; তাই sn = sn−1 + sn−2, s0 = s1 = 1, আর s10 = F11 = 89. খেয়াল করুন, এটি “00” ছাড়া string-এর মতো একই recurrence; দুটির মাঝে একটি bijection খোঁজা একটি ভালো অনুশীলন।
ধাঁধা ৩: উত্তল অষ্টভুজের কর্ণগুলো ভেতরে কয়টি বিন্দুতে কাটে?
শর্ত: কোনো তিনটি কর্ণ এক বিন্দুতে মেলে না। তখন ভেতরের প্রতিটি ছেদবিন্দু ঠিক দুটি কর্ণের, আর দুটি পরস্পর ছেদী কর্ণের চারটি প্রান্ত আলাদা শীর্ষবিন্দু। উল্টো দিকে যেকোনো ৪টি শীর্ষবিন্দু একটি উত্তল চতুর্ভুজ বানায়, যার দুই কর্ণ ঠিক একবার কাটে। Bijection: ছেদবিন্দু ↔ ৪ শীর্ষবিন্দুর সেট, তাই C(8, 4) = 70. শর্তটি জরুরি: নিয়মিত অষ্টভুজে চারটি লম্বা কর্ণ কেন্দ্রে একসঙ্গে মেলে, তাই সেখানে ছেদবিন্দু 70-এর কম। উপপাদ্যের শর্ত বাদ দিলে সূত্র ভুল হয়ে যায়, এটি তার একটি পরিষ্কার উদাহরণ।
ধাঁধা ৪: একটি ছোট দলে কি সবাই বিজোড় সংখ্যক বন্ধু রাখতে পারেন?
৭ জনের একটি দলে প্রত্যেকের বন্ধুসংখ্যা (দলের ভেতরে, বন্ধুত্ব দুই দিকে) বিজোড়, এটি সম্ভব কি? না: degree-র যোগফল হতো সাতটি বিজোড় সংখ্যার যোগ, যা বিজোড়, অথচ double counting বলে এটি 2 × (বন্ধুত্বের সংখ্যা), জোড়। কোনো উদাহরণ খোঁজার দরকারই নেই; গণনা নিজেই অসম্ভবতা প্রমাণ করে।
৮ · পরীক্ষার অঙ্ক আর গণনার একটি ভুল প্রমাণ
বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর লিখিত পরীক্ষায় এই পর্বের ধাঁচে যে অঙ্ক আসে, তার কয়েকটি পূর্ণ সমাধান। প্রতিটি সংখ্যা scratch-এ Python দিয়ে brute force-এ মেলানো।
অঙ্ক ১: ভোট গোনা
একটি ক্লাব নির্বাচনে A পেয়েছেন ৬ ভোট, B পেয়েছেন ৪ ভোট। কতগুলো গোনার ক্রমে A প্রতিটি ভোটের পর কঠোরভাবে এগিয়ে, আর সব ক্রম সমান সম্ভাব্য হলে তার সম্ভাবনা কত?
- সব ক্রম: ১০টি জায়গার কোন ৪টিতে B, তাই C(10, 4) = 210.
- Ballot theorem: ((6 − 4)/(6 + 4)) · 210 = 42; বা C(9, 4) − C(9, 3) = 126 − 84 = 42.
- সম্ভাবনা 42/210 = 1/5 = (a − b)/(a + b).
অঙ্ক ২: একটি মোড় দিয়ে যেতেই হবে, আরেকটি এড়াতে হবে
(0, 0) থেকে (6, 4)-এর কতগুলো lattice path P = (2, 1) দিয়ে যায় কিন্তু Q = (4, 2) দিয়ে যায় না?
- P দিয়ে যাওয়া পথ: C(3, 1) × C(7, 3) = 3 × 35 = 105.
- P আর Q দুটি দিয়েই: (0, 0) → P → Q → (6, 4) = C(3, 1) × C(3, 1) × C(4, 2) = 3 × 3 × 6 = 54. (Q থেকে P-তে যাওয়া যায় না, তাই ক্রম একটিই।)
- উত্তর 105 − 54 = 51.
অঙ্ক ৩: Composition
10-কে ঠিক ৪টি ধনাত্মক অংশে ক্রমসহ কতভাবে লেখা যায়, আর মোট কয়টি composition আছে? উত্তর C(9, 3) = 84 আর 29 = 512.
প্রমাণের ভুল ধরুন: “অর্ধেক পথ কর্ণের নিচে”
একজন লিখলেন: “দাবি: (0, 0) থেকে (n, n)-এর যে পথ কখনো কর্ণের ওপরে যায় না, তা C(2n, n)/2টি। প্রমাণ: প্রতিটি পথের R আর U অদলবদল করলে কর্ণের সাপেক্ষে তার প্রতিফলন পাওয়া যায়। এই প্রতিফলন কর্ণের নিচের পথকে ওপরের পথে নেয়, আর উল্টোটাও। তাই নিচের আর ওপরের পথ সমান সংখ্যক, প্রত্যেকে মোটের অর্ধেক। ∎” পড়ে ভুলটি নিজে খুঁজুন।
ভুল কোথায়: প্রমাণটি ধরে নিয়েছে যে প্রতিটি পথ হয় পুরো নিচে, নয়তো পুরো ওপরে। আসলে বেশিরভাগ পথ দুই দিকেই যায়, যেমন RUUR প্রথমে কর্ণের নিচে, তারপর কর্ণের ওপরে, শেষে আবার কর্ণে ফেরে। পুরো প্রতিফলন “নিচের পথ” আর “ওপরের পথ”-কে ঠিকই মেলায়, কিন্তু এই দুই দল মিলে সব পথ হয় না; মাঝখানে বড় একটি তৃতীয় দল থেকে যায়। Counterexample: n = 3-এ দাবি বলে 20/2 = 10, কিন্তু আসল সংখ্যা 5 (stepper ২ আর Python ব্লকে যাচাই করা)। n = 1-এ দাবিটি কাকতালীয়ভাবে ঠিক (2/2 = 1), তাই শুধু সবচেয়ে ছোট ক্ষেত্রে মিলিয়ে নিশ্চিন্ত হওয়া বিপজ্জনক। ঠিক কৌশল হলো reflection principle: পুরো পথ নয়, শুধু প্রথম ছোঁয়ার পরের অংশ উল্টানো, আর রেখা y = x নয়, y = x + 1.
আরও গভীরে: Reflection principle সম্ভাবনা তত্ত্বে simple random walk আর Brownian motion-এর সর্বোচ্চ মান হিসাবের একটি মূল হাতিয়ার। যদি পথ কর্ণ ছুঁতে পারে কিন্তু পেরোতে না পারে, আর ধাপে তির্যক (1, 1)-ও চলে, তখন Schröder সংখ্যা আসে; সব দিকে তির্যক ধাপসহ গ্রিড-পথ গোনে Delannoy সংখ্যা, যেমন (3, 3)-এ 63টি। আরও সাধারণভাবে, একাধিক পথ যাতে পরস্পর না ছোঁয়, এমন পথ-পরিবার গোনার Lindström–Gessel–Viennot lemma একটি determinant দিয়ে উত্তর দেয়, যেখানে গণনা আর রৈখিক বীজগণিত এক বিন্দুতে মেলে।
পরীক্ষা ও ইন্টারভিউতে: বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন প্রায়ই আসে:
- m × n গ্রিডে বাঁ-নিচ থেকে ডান-ওপরে সবচেয়ে ছোট পথ কয়টি? C(m + n, n); যেমন 4 × 3-এ 35.
- ৪ জোড়া বন্ধনী কতভাবে মেলানো যায়? C₄ = C(8, 4)/5 = 14.
- দৈর্ঘ্য 7-এর কতগুলো bit string-এ পরপর দুটি 0 নেই? F₉ = 34.
- ৬-এর composition কয়টি? 25 = 32.
- ৯ জনের প্রত্যেকে ঠিক ৫ জনের সঙ্গে করমর্দন সম্ভব? না, 9 × 5 = 45 বিজোড়।
৯ · AI-এর যুগে — গণনার প্রমাণ আর উত্তর যাচাই
গণনার প্রশ্নে AI সহকারী প্রায়ই আত্মবিশ্বাসী আর সুন্দর একটি উত্তর দেয়, যার ভুল লুকিয়ে থাকে মডেলে বা একটি ছোট ধাপে। এই পর্বের বিষয়ে কয়েকটি ভুল নিয়মিত দেখা যায়:
| AI-এর ভুল | কীভাবে ধরবেন |
|---|---|
| Reflection-এ ভুল রেখা (y = x) বা পুরো পথ উল্টানো; ফলে C(2n, n)/2-এর মতো উত্তর | n = 3-এ সব ২০টি পথ লিখে গুনুন; উত্তর 5 না এলে প্রমাণে ভুল |
| Catalan-এ off-by-one: C(2n, n)/n বা Cn+1-কে Cn বলা | n = 0, 1, 2, 3-এ 1, 1, 2, 5 আসে কি না দেখুন |
| দুটি নিষিদ্ধ বিন্দুতে শুধু দুবার বিয়োগ, দুটি দিয়েই যাওয়া পথ ফেরত যোগ না করা | বাধার ঘরে 0 বসিয়ে DP চালিয়ে মেলান |
| “Bijection” বলা, কিন্তু ফাংশন one-to-one বা onto নয় | ছোট n-এ উভয় দিকের ফাংশন কোডে লিখে ছবির সংখ্যা গুনুন |
| Double counting-এ দুই দিকে আসলে ভিন্ন সেট গোনা | প্রতিটি দিকে ঠিক কোন জোড়া গোনা হচ্ছে, তা এক লাইনে লিখতে বলুন |
| শর্ত বাদ দিয়ে সূত্র (যেমন “কোনো তিনটি কর্ণ এক বিন্দুতে নয়”) বা বানানো ইতিহাস | শর্তগুলো আলাদা করে চান; সাল আর নাম বিশ্বস্ত উৎসে মেলান |
AI-এর কাছে গণনার প্রমাণ চাইলে এমন কাঠামো দিন, যাতে প্রতিটি ধাপ আলাদা করে পরীক্ষা করা যায়:
AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট
গণনার প্রমাণ পরীক্ষার সবচেয়ে সস্তা উপায় হলো ছোট ক্ষেত্রে সব বস্তু তালিকা করা। AI যত সুন্দর যুক্তিই দিক, n = 3 বা 4-এ এক সেকেন্ডের brute force প্রায় প্রতিটি ভুল ধরে ফেলে; আর যেখানে মেলে না, সেখানেই প্রমাণের দুর্বল ধাপটি খুঁজুন।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১Fibonacci-র বইয়ের কয়েক শতাব্দী আগে ভারতীয় ছন্দশাস্ত্রে একই সংখ্যাগুলো এসেছিল বলে কেন বলা হয়? “00” ছাড়া bit string আর ছন্দের মাত্রার মধ্যে মিলটি কী?
সংস্কৃত আর প্রাকৃত ছন্দে অক্ষর দুই রকম: লঘু (এক মাত্রা) আর গুরু (দুই মাত্রা)। ছন্দবিদদের একটি স্বাভাবিক প্রশ্ন ছিল: মোট n মাত্রার কতগুলো আলাদা ছন্দ-নকশা সম্ভব? এটি ঠিক “1 আর 2 দিয়ে n-এর composition কয়টি”, অর্থাৎ §৭-এর সিঁড়ির ধাঁধা। শেষ অক্ষর লঘু হলে বাকি n − 1 মাত্রা, গুরু হলে n − 2 মাত্রা; তাই উত্তর আগের দুটির যোগ, Fibonacci-র recurrence. ইতিহাসবিদেরা সাধারণত Virahanka (আনুমানিক ৬ষ্ঠ থেকে ৮ম শতক), পরে Gopala আর জৈন পণ্ডিত Hemachandra (আনুমানিক ১১৫০ সাল)-র নাম উল্লেখ করেন, যাঁদের লেখায় এই নিয়ম স্পষ্ট; তারিখগুলো উৎসভেদে কিছুটা আলাদা। Leonardo of Pisa (Fibonacci)-র Liber Abaci (১২০২) খরগোশের গল্পে একই ধারা ইউরোপে পরিচিত করে, আর অনেক পরে ধারাটির নাম তাঁর নামে হয়।
“00” ছাড়া bit string-এর সঙ্গে মিলটি একটি সুন্দর bijection. দৈর্ঘ্য n − 1-এর এমন একটি string নিন, যাতে “00” নেই, আর শেষে একটি 1 জুড়ে দিন। এখন প্রতিটি 0-এর ঠিক পরে একটি 1 আছে, তাই string-টি “1” আর “01” টুকরায় অনন্যভাবে ভাঙে। “1”-কে লঘু (১ মাত্রা) আর “01”-কে গুরু (২ মাত্রা) পড়ুন: মোট মাত্রা n, আর উল্টো পথও স্পষ্ট। তাই n মাত্রার ছন্দ an−1 = Fn+1টি (scratch-এ n ≤ 11 পর্যন্ত মেলানো)। গণনার ভাষায় একই গাণিতিক বস্তু দুটি ভিন্ন সংস্কৃতিতে, ভিন্ন প্রয়োজনে আবিষ্কৃত হয়েছে।
এই ইতিহাস আরেকটি শিক্ষা দেয়: গণিতের ধারণার নাম প্রায়ই প্রথম আবিষ্কারকের নয়, বরং যিনি তা বিস্তৃত পাঠকের কাছে পৌঁছে দিয়েছেন তাঁর। “Pascal-এর ত্রিভুজ”, “Fibonacci ধারা” দুটিই এমন উদাহরণ (পর্ব ৩৪-এ Pingala-র মেরু-প্রস্তারের কথাও আছে)। তবে ইতিহাস লেখার সময় সাবধান থাকুন: জনপ্রিয় লেখায় কখনো তারিখ বা দাবি অতিরঞ্জিত হয়, তাই নির্ভরযোগ্য গণিত-ইতিহাসের বই দেখে নিন।
মূল উপলব্ধি: ক্ষেত্রভাগ করে recurrence বানানো একটি সর্বজনীন কৌশল; ছন্দের মাত্রা, সিঁড়ির ধাপ আর বিটের সারি একই গাণিতিক কাঠামোর তিনটি গল্প।
প্র ০২বন্ধনী, binary tree, বহুভুজের triangulation আর কর্ণ না পেরোনো পথ, এত আলাদা বস্তু একই Catalan সংখ্যায় কেন মেলে? এর ইতিহাস কী?
সব Catalan বস্তুর একটি সাধারণ গঠন আছে: প্রতিটি বস্তু একটি “প্রথম অংশ” দিয়ে অনন্যভাবে দুটি ছোট, স্বাধীন একই ধরনের বস্তুতে ভাঙে। বন্ধনীতে প্রথম “(”-এর জোড়া, binary tree-তে root-এর বাঁ আর ডান subtree, triangulation-এ একটি নির্দিষ্ট বাহুর ওপরের ত্রিভুজ, Dyck path-এ কর্ণে প্রথম ফেরা। এই ভাঙন থেকে সবসময় একই recurrence Cn+1 = ∑ CiCn−i আসে, আর শুরুর মান C0 = 1 একই। একই recurrence আর একই শুরু মানে একই ধারা, যা strong induction দিয়ে সহজেই প্রমাণ হয়। তবে recurrence মেলানোর চেয়ে আরও সুন্দর হলো সরাসরি bijection, যা একটি বস্তুকে আরেকটিতে রূপান্তর করে, যেমন tree থেকে বন্ধনী: “( বাঁ ) ডান”.
ইতিহাস সাধারণত এভাবে বলা হয়। Leonhard Euler আনুমানিক ১৭৫১ সালে Christian Goldbach-কে লেখা চিঠিতে উত্তল বহুভুজের triangulation গোনেন আর 1, 2, 5, 14, 42, …-এর একটি সূত্র অনুমান করেন। Johann Segner ১৭৫৮ সালের দিকে recurrence দেন। মঙ্গোলীয় গণিতবিদ Minggatu (Ming Antu) ১৭৩০-এর দশকে ত্রিকোণমিতির ধারায় এই সংখ্যাগুলো ব্যবহার করেছিলেন বলে ইতিহাসবিদেরা উল্লেখ করেন। Eugène Catalan ১৮৩৮ সালে বন্ধনী বসানোর প্রশ্ন নিয়ে লেখেন, আর বিশ শতকে নামটি তাঁর নামে স্থায়ী হয়। Richard Stanley-র “Catalan Numbers” (২০১৫) বইয়ে এই সংখ্যার দুই শতাধিক আলাদা ব্যাখ্যা সংকলিত আছে।
কম্পিউটার বিজ্ঞানে এর ব্যবহারিক অর্থ আছে। n-টি matrix গুণের ক্রম, n চাবির BST-এর আকার, একটি expression-এর parse tree, stack-এ push/pop-এর বৈধ ক্রম, সবই Catalan সংখ্যক। Cn মোটামুটি 4n/(n3/2√π) হারে বাড়ে, তাই সব সম্ভাবনা চেষ্টা করা দ্রুত অসম্ভব হয়ে যায়, আর DP-র মতো কৌশল লাগে। Sloane-এর OEIS (Online Encyclopedia of Integer Sequences)-এ Catalan ধারার নম্বর A000108; অচেনা একটি ধারার প্রথম কয়েকটি পদ সেখানে খুঁজলে প্রায়ই জানা যায় এটি কোন পরিচিত বস্তু গোনে।
মূল উপলব্ধি: একই ভাঙনের কাঠামো থাকলে একই সংখ্যা আসে; bijection খুঁজে পাওয়া মানে দুটি সমস্যা আসলে একটিই সমস্যা।
প্র ০৩Ballot problem আর reflection principle-এর আবিষ্কার নিয়ে প্রচলিত গল্পটি কতটা ঠিক? আর random walk-এ এই কৌশল কেন এত গুরুত্বপূর্ণ?
Ballot problem-টি ফরাসি গণিতবিদ Joseph Bertrand ১৮৮৭ সালে প্রকাশ্যে প্রশ্ন হিসেবে তোলেন, আর একই বছর Désiré André একটি সুন্দর গণনামূলক প্রমাণ দেন। তবে ইংরেজ গণিতবিদ William Whitworth আরও আগে (১৮৭০-এর দশকে) প্রায় একই ফল লিখেছিলেন বলে উল্লেখ করা হয়। বহু পাঠ্যবইয়ে “André-র reflection principle” লেখা থাকে, কিন্তু গণিত-ইতিহাসবিদেরা দেখিয়েছেন যে André-র মূল যুক্তিতে আসলে জ্যামিতিক প্রতিফলন ছিল না; তিনি একটি ভিন্ন অদলবদলের bijection ব্যবহার করেছিলেন, আর “প্রতিফলন” ছবিটি পরে অন্যদের হাতে জনপ্রিয় হয়। তাই নামটি সুবিধাজনক, কিন্তু কৃতিত্বের গল্পটি সরল করা।
Random walk-এ গুরুত্বের কারণ সহজ। প্রতিটি ধাপে +1 বা −1 নেওয়া একটি হাঁটা ঠিক একটি lattice path, শুধু ৪৫ ডিগ্রি ঘোরানো। “হাঁটা কখনো শূন্যের নিচে যায় না”, “সর্বোচ্চ মান অন্তত k”, “প্রথমবার শূন্যে ফেরা কখন”, এ ধরনের প্রশ্নের উত্তর reflection principle সরাসরি দেয়। জুয়াড়ির দেউলিয়া হওয়ার সম্ভাবনা, শেয়ারের দামের মডেলে একটি সীমা ছোঁয়ার সম্ভাবনা, আর একটি queue কখনো খালি হবে কি না, সবখানে এই যুক্তি আসে। Brownian motion-এর জন্যও একটি অবিচ্ছিন্ন reflection principle আছে, যা সম্ভাবনা তত্ত্বের একটি মৌলিক ফল।
কম্পিউটার বিজ্ঞানে একটি সরাসরি প্রয়োগ হলো stack. n-টি push আর n-টি pop-এর একটি ক্রম বৈধ তখনই, যখন কোনো মুহূর্তে pop বেশি নেই, অর্থাৎ Dyck path. এলোমেলোভাবে push/pop সাজালে বৈধ হওয়ার সম্ভাবনা Cn/C(2n, n) = 1/(n + 1), যা ballot theorem-এর একটি রূপ। এই ধরনের হিসাব parser আর memory allocator বিশ্লেষণে কাজে আসে। পর্ব ৩৮ থেকে সম্ভাবনা নিয়ে কাজ শুরু হলে এই সম্পর্কগুলো আরও স্পষ্ট হবে।
মূল উপলব্ধি: একটি ভালো bijection একটি “কখনো পেরোবে না” শর্তকে সরল বিয়োগে বদলে দেয়; আর ইতিহাসে কৃতিত্বের গল্প প্রায়ই সূত্রের চেয়ে কম নির্ভুল।
প্র ০৪Double counting কি শুধু সুন্দর সমতা প্রমাণের খেলা, নাকি algorithm বিশ্লেষণেও কাজে লাগে? Euler-এর ১৭৩৬ সালের লেখার সঙ্গে এর সম্পর্ক কী?
Leonhard Euler ১৭৩৬ সালে Königsberg-এর সেতুর সমস্যা নিয়ে যে লেখাটি লেখেন, সেটিকে গ্রাফ তত্ত্বের সূচনা ধরা হয়। সেখানে তিনি লক্ষ করেন যে প্রতিটি সেতু দুটি স্থলভাগকে ছোঁয়, তাই সব স্থলভাগের সেতু-সংখ্যার যোগ সেতুর সংখ্যার দ্বিগুণ। এটিই handshake lemma-র মূল ভাব, আর এটি সরাসরি double counting: (স্থলভাগ, সেতু) জোড়া দুইভাবে গোনা। এখান থেকে তিনি বের করেন, বিজোড় সংখ্যক সেতুর স্থলভাগ জোড় সংখ্যক হতে বাধ্য, আর সব সেতু একবার করে পার হওয়ার শর্ত তৈরি হয়। গ্রাফের পূর্ণ আলোচনা পর্ব ৫৭ থেকে।
Algorithm বিশ্লেষণে double counting প্রতিদিনের হাতিয়ার। Graph-এর BFS বা DFS-এর সময় কেন O(V + E)? কারণ adjacency list-এ প্রতিটি vertex-এর তালিকা একবার দেখা হয়, আর সব তালিকার মোট দৈর্ঘ্য ∑ deg(v) = 2E (undirected গ্রাফে)। “প্রতিটি vertex-এর জন্য কত কাজ” যোগ করার বদলে “প্রতিটি edge কতবার ছোঁয়া হলো” গুনলে উত্তর সহজ হয়। DSA কোর্সের পর্ব ৩৯-এ এই হিসাব আসে। Amortized analysis-এও একই ভাব: n-টি operation-এর মোট খরচ প্রতিটি operation ধরে না গুনে, প্রতিটি উপাদান মোট কতবার সরানো হলো তা গোনা।
অসম্ভবতার প্রমাণেও double counting শক্তিশালী। একটি সমস্যা মেটানোর কোনো ব্যবস্থা আছে কি না প্রশ্নে, দুইভাবে গুনে যদি একটি জোড় আর একটি বিজোড় পাওয়া যায়, তাহলে উদাহরণ খোঁজার দরকারই নেই। Block design-এর মতো জটিল গঠনে (যেমন টুর্নামেন্টের সূচি, যেখানে প্রতি জোড়া দল ঠিক একবার একই গ্রুপে পড়বে) প্রথম পরীক্ষাটিই হয় double counting দিয়ে, কোন মাপের গঠন অন্তত সম্ভব।
মূল উপলব্ধি: যোগের ক্রম বদলানো, অর্থাৎ সারি ধরে না গুনে কলাম ধরে গোনা, প্রমাণ আর algorithm বিশ্লেষণ দুই জায়গাতেই জটিল হিসাবকে সরল করে।
প্র ০৫দুটি DNA বা দুটি বাক্য মেলানোর (alignment) সব উপায় কি গোনা যায়? Lattice path এখানে কীভাবে আসে, আর সব উপায় চেষ্টা না করে সেরাটি পাওয়া যায় কীভাবে?
দুটি sequence, ধরুন দৈর্ঘ্য m আর n, পাশাপাশি সাজানোকে বলে alignment: প্রতিটি ধাপে হয় দুই দিক থেকে একটি করে অক্ষর মেলানো হয়, নয়তো এক দিকে ফাঁক রেখে অন্য দিক থেকে একটি অক্ষর নেওয়া হয়। গ্রিডে এটি (0, 0) থেকে (m, n)-এর একটি পথ, যার ধাপ R (প্রথম sequence থেকে একটি অক্ষর, অন্যটিতে ফাঁক), U (উল্টো), আর তির্যক (1, 1) (দুটি অক্ষর মেলানো)। এই পথগুলো গোনে Delannoy সংখ্যা, ফরাসি সেনা কর্মকর্তা ও শৌখিন গণিতবিদ Henri Delannoy-র নামে (উনিশ শতকের শেষ ভাগ)। ছোট উদাহরণ: দুটি ৩ অক্ষরের শব্দের alignment 63টি, আর দুটি ১০ অক্ষরের শব্দের ৮০ লাখের বেশি (৮০,৯৭,৪৫৩টি, scratch-এ recurrence দিয়ে হিসাব করা)। দৈর্ঘ্য কয়েক হাজার হলে সংখ্যাটি জ্যোতির্বিদ্যার মাপও ছাড়িয়ে যায়।
তবু সেরা alignment পাওয়া যায়, কারণ পথের স্কোর ধাপগুলোর স্কোরের যোগ। তাহলে (x, y) পর্যন্ত সেরা স্কোর = তিনটি আগের ঘরের (বাঁ, নিচ, তির্যক) সেরা স্কোর থেকে এক ধাপ, ঠিক §১-এর N(x, y) = N(x − 1, y) + N(x, y − 1)-এর মতো, শুধু “যোগ”-এর জায়গায় “সর্বোচ্চ”। Saul Needleman আর Christian Wunsch ১৯৭০ সালে প্রোটিন sequence মেলানোর জন্য এই DP প্রকাশ করেন; edit distance আর LCS একই কাঠামো, যা DSA কোর্সের পর্ব ৫৪-এ কোডসহ আছে। গ্রিডে ঘর m × n-টি, তাই সময় O(mn), যদিও পথ সূচকীয় সংখ্যক।
এখানে গণনা আর algorithm-এর সম্পর্কটি পরিষ্কার: গণনা বলে brute force কেন অসম্ভব (পথ অসংখ্য), আর একই গ্রিডের recurrence বলে কীভাবে তা এড়ানো যায় (ঘর অল্প)। পথ গোনার DP আর সেরা পথ খোঁজার DP আসলে একই টেবিলে দুটি ভিন্ন “যোগ” (সাধারণ যোগ বনাম সর্বোচ্চ), যা উচ্চতর গণিতে semiring-এর ধারণা দিয়ে একসঙ্গে বলা হয়।
মূল উপলব্ধি: বস্তু অসংখ্য হলেও তাদের গঠন যদি ছোট উপসমস্যায় ভাঙে, তবে গোনা আর সেরাটি খোঁজা দুটোই একই recurrence-এ দ্রুত করা যায়।
অনুশীলন
আগে খাতায় করুন, তারপর উত্তর খুলে মেলান।
-
প্রমাণ: Cn দিয়ে (0, 0) থেকে (n, n)-এর কর্ণের ওপরে না যাওয়া পথের সংখ্যা বোঝালে প্রমাণ করুন, n ≥ 0 হলে Cn+1 = ∑i=0n Ci Cn−i. (ইঙ্গিত: (0, 0)-এর পর পথটি প্রথম কোথায় কর্ণে ফেরে?)
- (n + 1, n + 1)-এর একটি ভালো পথ P নিন। P শেষে কর্ণে থাকে, তাই (0, 0)-এর পর P প্রথম যে কর্ণবিন্দুতে ফেরে, সেটি (i + 1, i + 1), যেখানে 0 ≤ i ≤ n ঠিক একটি মান। এভাবে সব ভালো পথ n + 1টি পরস্পর বিচ্ছিন্ন দলে ভাগ হয়। — ক্ষেত্রভাগ
- প্রথম ফেরার আগে পথটি কর্ণ ছোঁয় না, তাই প্রথম ধাপ R আর (i + 1, i + 1)-এ পৌঁছানোর শেষ ধাপ U. মাঝের অংশ (1, 0) থেকে (i + 1, i)-এ যায় আর রেখা y = x − 1-এর ওপরে ওঠে না; এক ঘর বাঁয়ে সরালে এটি (0, 0) থেকে (i, i)-এর একটি ভালো পথ। উল্টো দিকেও এভাবে মেলে, তাই এই অংশ Ci উপায়ে। — bijection
- (i + 1, i + 1) থেকে (n + 1, n + 1)-এর বাকি অংশ কর্ণের ওপরে যায় না, তাই এটি (0, 0) থেকে (n − i, n − i)-এর একটি ভালো পথের সরানো রূপ: Cn−i উপায়। — bijection
- দুই অংশ স্বাধীন, তাই i-তম দলে CiCn−iটি পথ (গুণের নিয়ম); সব দল যোগ করলে (যোগের নিয়ম) দাবি। ∎
যাচাই: C₄ = C₀C₃ + C₁C₂ + C₂C₁ + C₃C₀ = 5 + 2 + 2 + 5 = 14 ✓; scratch-এ n ≤ 10 পর্যন্ত মেলানো।
-
হিসাব: (ক) (0, 0) থেকে (6, 4)-এর কতগুলো lattice path P = (2, 1) আর Q = (4, 2) দুটি বিন্দুই এড়ায়? (খ) দৈর্ঘ্য 8-এর কতগুলো bit string-এ অন্তত একবার পরপর দুটি 0 আছে?
(ক) মোট C(10, 4) = 210. P দিয়ে: C(3, 1) × C(7, 3) = 105. Q দিয়ে: C(6, 2) × C(4, 2) = 15 × 6 = 90. দুটি দিয়েই: 3 × C(3, 1) × 6 = 54. Inclusion–exclusion: 210 − 105 − 90 + 54 = 69. শুধু 210 − 105 − 90 = 15 লিখলে দুটি দিয়ে যাওয়া ৫৪টি পথ দুবার বাদ পড়ে। বাধার ঘরে 0 বসানো DP-ও 69 দেয়।
(খ) Complement: 28 − a8 = 256 − F10 = 256 − 55 = 201. দুটি সংখ্যাই scratch-এ brute force দিয়ে যাচাই করা।
-
আউটপুট অনুমান: কোড না চালিয়ে বলুন কী ছাপা হবে। তারপর যেকোনো Python-এ চালিয়ে মেলান।
from math import comb cat = [comb(2*n, n) // (n + 1) for n in range(6)] print(cat) print(comb(6, 3) - comb(6, 4)) print(sum(1 for k in range(1, 6) for _ in range(comb(4, k - 1))))[1, 1, 2, 5, 14, 42] 5 16প্রথম লাইন C₀ থেকে C₅. দ্বিতীয় লাইন reflection-এর বিয়োগ C(6, 3) − C(6, 4) = 20 − 15 = 5 = C₃. তৃতীয় লাইন k = 1 থেকে 5 পর্যন্ত C(4, k − 1) যোগ করে, অর্থাৎ 5-এর k অংশের composition-এর সংখ্যার যোগফল: 1 + 4 + 6 + 4 + 1 = 16 = 24, উপপাদ্য ৪.১ অনুযায়ী। আউটপুট CPython-এ চালিয়ে মেলানো।
-
প্রমাণের ভুল ধরুন: একজন লিখলেন: “দাবি: ৭ জনের একটি দল এমনভাবে সাজানো যায় যে প্রত্যেকে ঠিক ৩ জনের সঙ্গে করমর্দন করেন। গঠন: ৭ জনকে একটি বৃত্তে বসান; প্রত্যেকে দুই পাশের দুজনের সঙ্গে আর ঠিক উল্টো দিকে বসা মানুষটির সঙ্গে করমর্দন করবেন। তাহলে প্রত্যেকের ৩টি করমর্দন। ∎” কোথায় ভুল?
- গঠনের ভুল: ৭ বিজোড়, তাই বৃত্তে কারও “ঠিক উল্টো দিকে” কেউ বসেন না; ব্যক্তি ১-এর উল্টো দিকে ৪ আর ৫-এর মাঝখানের ফাঁক। আর যদি কাছাকাছি একজনকে বেছে নেওয়া হয়, তবে কাউকে না কাউকে দুজন বেছে নেবেন, তার করমর্দন ৪টি হবে।
- দাবিটিই অসম্ভব: সবার করমর্দনের যোগফল 7 × 3 = 21, বিজোড়; কিন্তু double counting অনুযায়ী এটি 2 × (করমর্দনের সংখ্যা), জোড়। তাই কোনো গঠনই কাজ করবে না।
- শিক্ষা: জোড় সংখ্যক মানুষে (যেমন ৮ জন) এই বৃত্ত-গঠন ঠিকই কাজ করে; বিজোড়ে প্রমাণ যাচাই না করে গঠনটি নকল করলেই ভুল। “আছে” দাবির আগে একটি দ্রুত গণনামূলক পরীক্ষা করে নিন।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমReflection principle দিয়ে কর্ণের ওপরে না যাওয়া পথ গোনার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- (0, 0) থেকে (n, n)-এর সব পথ গোনা: C(2n, n)
- খারাপ পথ চেনা: যে পথ y = x + 1 রেখা ছোঁয়
- প্রথম ছোঁয়ার পরের অংশে R আর U অদলবদল করা
- দেখানো যে নতুন পথগুলো ঠিক (n − 1, n + 1)-এর সব পথ, এক-এক মিলে
- খারাপ পথের সংখ্যা C(2n, n + 1) বিয়োগ করা
- সরল করে C(2n, n)/(n + 1) পাওয়া
একটি 4 × 3 রাস্তার গ্রিডে বাঁ-নিচের কোণ (0, 0) থেকে ডান-ওপরের কোণ (4, 3)-এ শুধু ডানে আর ওপরে গেলে কতগুলো আলাদা পথ আছে?
- 12
- 128
- 35
- 5040
৪ জোড়া বন্ধনী দিয়ে কতগুলো আলাদা মেলানো (balanced) সারি লেখা যায়?
- 70
- 14
- 35
- 42
6-কে ধনাত্মক পূর্ণসংখ্যার ক্রমসহ যোগফল হিসেবে (যেমন 2 + 1 + 3, 1 + 2 + 3 আলাদা ধরে) কতভাবে লেখা যায়?
- 11
- 64
- 10
- 32
দৈর্ঘ্য 7-এর কতগুলো bit string-এ পরপর দুটি 0 (“00”) নেই?
- 34
- 21
- 94
- 128
A পেয়েছেন ৪ ভোট, B পেয়েছেন ২ ভোট। ভোটগুলো একটি একটি করে গোনা হলে কতগুলো গোনার ক্রমে A প্রতিটি ভোটের পর কঠোরভাবে এগিয়ে থাকেন?
- 15
- 10
- 5
- 3
৯ জনের একটি দলে প্রত্যেকে ঠিক ৫ জনের সঙ্গে করমর্দন করবেন (একই দুজন একবারই), এমন ব্যবস্থা নিয়ে কোনটি সত্য?
- সম্ভব, আর করমর্দন হবে 45টি
- অসম্ভব, কারণ 9 × 5 = 45 বিজোড়
- সম্ভব, আর করমর্দন হবে 22টি
- অসম্ভব, কারণ দলে মানুষ খুব কম
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Lattice path: (0, 0) থেকে (m, n)-এ C(m + n, n)টি পথ; একটি বিন্দু এড়াতে মোট − (সেখান পর্যন্ত × সেখান থেকে)। DP: N(x, y) = N(x − 1, y) + N(x, y − 1).
- Reflection: প্রথম ছোঁয়ার পরের অংশ উল্টালে খারাপ পথ ↔ (n − 1, n + 1)-এর পথ; তাই Cn = C(2n, n)/(n + 1), আর ballot-এ ((a − b)/(a + b)) · C(a + b, a).
- Catalan: বন্ধনী, binary tree, triangulation, stack-এর ক্রম; সবার পেছনে একই ভাঙন আর Cn+1 = ∑ CiCn−i.
- Bijection আর double counting: composition ↔ subset দিয়ে 2n−1; একই সেট দুইভাবে গুনে সমতা বা অসম্ভবতা (degree-র যোগফল জোড়)।
- Complement আর recurrence: “অন্তত একটি” = মোট − “একটিও না”; শেষ ধাপ ধরে ভাগ করলে recurrence, যেমন “00” ছাড়া string = Fn+2. সব উত্তর ছোট n-এ brute force দিয়ে মেলান।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৩৮ · বিচ্ছিন্ন সম্ভাবনা — sample space, event আর Laplace-এর সংজ্ঞাপরবর্তী পর্বগণনা থেকে সম্ভাবনা: সমান সম্ভাব্য ফলাফল আর ঘটনার সম্ভাবনা।
- পর্ব ৩২ · Pigeonhole principle — সহজ থেকে generalizedপুনরালোচনাঅস্তিত্বের প্রশ্নে গণনার আরেকটি কৌশল।
- ডিসক্রিট ম্যাথমেটিক্স · কোর্স হোম৮৩টি পর্বপুরো কোর্সের মানচিত্র আর ১৪টি মডিউল।
- DSA · পর্ব ৫২ · Dynamic programming-এর ভিত্তি — memoization আর tabulationকোডে প্রয়োগগ্রিড-পথ গোনার DP আর তার জটিলতা।
- DSA · পর্ব ৫৪ · DP-র ক্লাসিক সমস্যা ২ — LCS, edit distance আর matrix chainকোডে প্রয়োগAlignment-এর গ্রিড আর Catalan সংখ্যক বন্ধনী।