এই পর্বে যা শিখবেন
- Recurrence relation, তার order আর initial condition-এর সংজ্ঞা, আর initial condition দিলে সমাধান যে একটিই তার প্রমাণ
- ঋণ, খরগোশ, bit string, Hanoi, domino টালি, সমতলে রেখা, binary tree আর Josephus সমস্যাকে recurrence-এ রূপান্তর
- Unrolling (iteration) দিয়ে closed form অনুমান আর induction দিয়ে তার প্রমাণ
- Hanoi-র 2n − 1 যে কম করা যায় না, সেই lower bound-এর প্রমাণ, আর Josephus-এর binary সূত্রের প্রমাণ
- প্রতিটি মডেল brute force গণনা আর exact integer বা Fraction দিয়ে Python-এ মেলানো
১ · Recurrence relation — সংজ্ঞা, initial condition আর অনন্যতা
মডিউল ৭-এ আমরা সম্ভাবনা গুনেছি। এই মডিউলের নাম “উন্নত গণনা”, আর তার প্রথম হাতিয়ার একটি পুরোনো চেনা জিনিস: নিজের ছোট রূপ দিয়ে নিজেকে লেখা। পর্ব ১২ “Recursive সংজ্ঞা আর structural induction”-এ আপনি factorial, Fibonacci আর gcd-এর recursive সংজ্ঞা দেখেছেন। এই পর্বে প্রশ্নটি উল্টো দিক থেকে আসে: একটি গণনার সমস্যা হাতে, যার সরাসরি সূত্র চোখে পড়ে না। তাকে কীভাবে “আগের ধাপের উত্তর দিয়ে এই ধাপের উত্তর” আকারে লেখা যায়? এই লেখাটিই একটি মডেল।
রেলস্টেশনের সিঁড়ি ভাবুন। একজন যাত্রী এক ধাপে একটি বা দুটি সিঁড়ি ওঠেন। দশম সিঁড়িতে পৌঁছানোর কয়টি উপায়? সরাসরি সব উপায় লেখা কষ্টের। কিন্তু শেষ পা-টি দেখুন: হয় নবম সিঁড়ি থেকে এক ধাপ, নয়তো অষ্টম থেকে দুই ধাপ। তাই দশমে পৌঁছানোর উপায় = নবমে পৌঁছানোর উপায় + অষ্টমে পৌঁছানোর উপায়। পুরো সমস্যাটি দুটি ছোট সমস্যায় ভেঙে গেল। Recurrence দিয়ে মডেল বানানোর মূল কৌশল এটিই: শেষ (বা প্রথম) ধাপটি ধরে ক্ষেত্র ভাগ করা।
একটি ধারা a0, a1, a2, …-এর recurrence relation হলো এমন একটি নিয়ম, যা একটি নির্দিষ্ট n0 থেকে শুরু করে প্রতিটি n ≥ n0-এর জন্য an-কে আগের কিছু পদ দিয়ে প্রকাশ করে। নিয়মটি যদি an = f(n, an−1, an−2, …, an−k) আকারের হয়, অর্থাৎ বড়জোর k ধাপ পেছনে তাকায়, আর সত্যিই an−k ব্যবহার করে, তাহলে k হলো তার order.
ধারার যে প্রথম কয়েকটি পদ নিয়ম দিয়ে নয়, সরাসরি দেওয়া থাকে (যেমন a0, …, ak−1), সেগুলো initial condition. Recurrence মেনে চলে এমন যেকোনো ধারা তার একটি সমাধান; আর an-কে আগের পদ ছাড়া সরাসরি n দিয়ে লেখা সূত্র হলো closed form.
শুধু recurrence একটি ধারাকে ঠিক করে না। an = an−1 + an−2-এর একটি সমাধান Fibonacci ধারা 0, 1, 1, 2, 3, 5, 8, …, আবার আরেকটি সমাধান 2, 1, 3, 4, 7, 11, 18, …, যার নাম Lucas সংখ্যা. 0, 0, 0, … ধারাটিও একটি সমাধান। একই নিয়ম, ভিন্ন শুরু, সম্পূর্ণ ভিন্ন ধারা। তাই recurrence লিখলেই কাজ শেষ নয়; initial condition আর কোন n থেকে নিয়মটি খাটে, দুটিই লিখতে হয়।
k ≥ 1, আর f যেকোনো ফাংশন। c0, …, ck−1 যেকোনো k-টি মান হলে ঠিক একটি ধারা আছে, যার ai = ci (0 ≤ i ≤ k − 1) আর প্রতিটি n ≥ k-এর জন্য an = f(n, an−1, …, an−k).
- অস্তিত্ব: প্রথম k-টি পদ ci, আর n ≥ k-এর জন্য an-কে নিয়মটি দিয়েই সংজ্ঞায়িত করুন। ডান পাশে শুধু আগের পদ আছে, তাই এটি একটি বৈধ recursive সংজ্ঞা। — recursive সংজ্ঞা (পর্ব ১২)
- অনন্যতা, প্রস্তুতি: ধরুন a আর b দুটি ধারাই সব শর্ত মানে। দাবি P(n): an = bn. — প্রমাণের লক্ষ্য
- ভিত্তি: 0 ≤ n ≤ k − 1 হলে an = cn = bn. — initial condition
- ধাপ: n ≥ k, আর ধরুন সব m < n-এ P(m) সত্য। তাহলে an = f(n, an−1, …, an−k) = f(n, bn−1, …, bn−k) = bn, কারণ মাঝের সমতায় প্রতিটি সূচক n − 1, …, n − k ছোট। — strong induction-এর অনুমান
- পর্ব ১১-এর strong induction অনুযায়ী সব n ≥ 0-এ an = bn. ∎ — strong induction
ধাপ ৪-এ লক্ষ করুন, একসঙ্গে k-টি আগের পদ লাগে, তাই সাধারণ induction নয়, strong induction স্বাভাবিক পছন্দ। আর ধাপ ৩-এ ঠিক k-টি ভিত্তি লাগে: order 2-এর recurrence-এ শুধু a0 দিলে a1 যেকোনো মান নিতে পারে, আর প্রতিটি মান একটি আলাদা সমাধান দেয়। উল্টো দিকে, বেশি initial condition দিলে তারা পরস্পরবিরোধী হতে পারে: a0 = 0, a1 = 1, a2 = 5 আর an = an−1 + an−2 (n ≥ 2) একসঙ্গে কোনো ধারা মানে না।
- an কী গোনে, তা এক বাক্যে নিখুঁতভাবে লিখুন (যেমন “দৈর্ঘ্য n-এর এমন bit string-এর সংখ্যা, যাতে 00 নেই”)।
- n = 0, 1, 2, 3-এর মতো ছোট ক্ষেত্র হাতে গুনুন।
- প্রথম বা শেষ ধাপ ধরে সব বস্তুকে পরস্পর বিচ্ছিন্ন দলে ভাগ করুন; প্রতিটি দল একটি ছোট সমস্যার সমান কি না দেখুন।
- যোগ আর গুণের নিয়ম দিয়ে recurrence লিখুন, সঙ্গে initial condition আর “কোন n থেকে খাটে”।
- ছোট n-এ brute force গণনার সঙ্গে মিলিয়ে নিন, তারপর closed form খুঁজুন আর প্রমাণ করুন।
২ · প্রথম মডেল — বাজারঘরের ঋণ আর মাসিক জমা
বাজারঘর নতুন গুদামের তাক কিনতে ১০ লাখ টাকা ঋণ নিল। ধরা যাক মাসে 1% হারে সুদ যোগ হয়, আর প্রতি মাসের শেষে একটি নির্দিষ্ট কিস্তি P শোধ করা হয়। সুদের হারটি শুধু হিসাব বোঝানোর একটি কাল্পনিক সংখ্যা; বাস্তব ঋণের শর্ত, ফি আর গোলাকরণের নিয়ম আলাদা, আর এটি কোনো আর্থিক পরামর্শ নয়। প্রশ্ন: n মাস পরে বাকি কত, আর ১২ মাসে শোধ করতে P কত হওয়া উচিত?
Bn = n মাস পরে বাকি টাকা ধরুন। মাস শুরুর বাকি Bn−1-এর ওপর সুদ যোগ হয়ে (1 + r)Bn−1 হয়, তারপর P বাদ যায়। তাই মডেল:
B0 = 1000000, Bn = (1 + r)Bn−1 − P (n ≥ 1), r = 1/100
এটি order 1-এর recurrence, একটি initial condition যথেষ্ট। এখানে compound interest বা চক্রবৃদ্ধি সুদ লুকিয়ে আছে: সুদের ওপরও সুদ ধরে। এবার unrolling (iteration) করি, অর্থাৎ নিয়মটি বারবার নিজের ভেতরে বসাই। g = 1 + r লিখলে
Bn = gBn−1 − P = g2Bn−2 − gP − P = … = gnB0 − P(gn−1 + … + g + 1)
বন্ধনীর ভেতরটি একটি geometric series, যার যোগফল (gn − 1)/(g − 1) (পর্ব ১৭ “Sequence আর summation — closed form আর বিখ্যাত যোগফল”)। এভাবে একটি অনুমান পাওয়া গেল। অনুমান প্রমাণ নয়, তাই induction লাগে।
r ≠ 0, g = 1 + r আর Bn = gBn−1 − P (n ≥ 1) হলে প্রতিটি n ≥ 0-এ
Bn = gnB0 − P(gn − 1)/r
- ভিত্তি n = 0: ডান পাশ g0B0 − P · 0/r = B0. — সরাসরি
- অনুমান: কোনো n ≥ 1-এর জন্য Bn−1 = gn−1B0 − P(gn−1 − 1)/r. — inductive hypothesis
- ধাপ: Bn = gBn−1 − P = gnB0 − P(gn − g)/r − P = gnB0 − P(gn − g + r)/r. — recurrence আর অনুমান
- g − r = 1, তাই gn − g + r = gn − 1, আর Bn = gnB0 − P(gn − 1)/r. ∎ — বীজগণিত, পর্ব ১০
১২ মাসে শোধ মানে B12 = 0. সূত্রে বসিয়ে P = rB0g12/(g12 − 1). এই পদ্ধতিতে সমান কিস্তিতে ঋণ শোধকে বলে amortization. একই ছাঁচে উল্টো কাজও চলে: প্রতি মাসে 5000 টাকা জমা আর মাসে 1% লাভ ধরলে S0 = 0, Sn = (1 + r)Sn−1 + 5000, আর একই unrolling থেকে Sn = 5000(gn − 1)/r; ১২ মাসে প্রায় 63412.52 টাকা (scratch-এ যাচাই করা)। নিচের ব্লক ঋণের মডেলটি Fraction দিয়ে নির্ভুলভাবে চালায়। প্রথমবার চালালে ব্রাউজারে Python (Pyodide) নামতে কয়েক সেকেন্ড লাগে।
from fractions import Fraction
B0 = Fraction(1_000_000) # loan in taka (example only)
r = Fraction(1, 100) # 1% per month, illustrative
N = 12
P = B0 * r / (1 - (1 + r) ** -N) # payment that clears the loan in N months
print("exact payment P =", round(float(P), 4), "taka per month")
def closed(n): # the unrolled formula
g = (1 + r) ** n
return g * B0 - P * (g - 1) / r
B = B0
for n in range(1, N + 1):
B = (1 + r) * B - P # B(n) = (1 + r) B(n-1) - P
assert B == closed(n) # exact equality, no rounding
if n in (1, 2, 3, 6, 11, 12):
print(f"month {n:2}: balance {float(B):11.2f}")
print("recurrence == closed form for all", N, "months")
for pay in (88848, 88849): # payment rounded to whole taka
Bq = B0
for n in range(N):
Bq = (1 + r) * Bq - pay
print(f"pay {pay}: after 12 months {float(Bq):7.2f}")
print("total interest:", round(float(N * P - B0), 2))আসল আউটপুট (ব্রাউজারের Python):
exact payment P = 88848.7887 taka per month
month 1: balance 921151.21
month 2: balance 841513.93
month 3: balance 761080.29
month 6: balance 514921.06
month 11: balance 87969.10
month 12: balance 0.00
recurrence == closed form for all 12 months
pay 88848: after 12 months 10.00
pay 88849: after 12 months -2.68
total interest: 66185.46
নির্ভুল কিস্তি একটি ভগ্নাংশ, প্রায় 88848.79 টাকা। প্রতিটি মাসে recurrence আর closed form হুবহু সমান, কারণ Fraction কোনো গোলাকরণ করে না; assert একবারও ব্যর্থ হয়নি। লক্ষ করুন, প্রথম মাসে বাকি কমেছে প্রায় 78849 টাকা, কারণ কিস্তির একটি অংশ সুদে যায়; শেষের দিকে সুদ কম, তাই আসল বেশি কমে। বাস্তবে কিস্তি পূর্ণ টাকায় হয়: 88848 দিলে ১২ মাস পরে 10 টাকা বাকি থাকে, 88849 দিলে 2.68 টাকা বেশি দেওয়া হয়। তাই সাধারণত শেষ কিস্তিটি একটু সমন্বয় করা হয়। ছোট গোলাকরণও recurrence-এ বারবার গুণ হয়ে জমে, এটি মডেলের একটি বাস্তব সীমা।
এই ধরনের order 1-এর recurrence, যেখানে প্রতিবার একই ধ্রুবক যোগ বা বিয়োগ হয়, তার সাধারণ সমাধানের পদ্ধতি পর্ব ৪৬ “Non-homogeneous recurrence আর particular solution”-এ আসবে। আজ আমরা unrolling দিয়ে অনুমান আর induction দিয়ে প্রমাণের পথেই থাকব।
৩ · Fibonacci-র খরগোশ, ছন্দ আর “00”-হীন bit string
পিসার Leonardo, পরে যিনি Fibonacci নামে পরিচিত, ১২০২ সালের Liber Abaci বইয়ে একটি খরগোশের ধাঁধা দেন। আধুনিক পাঠ্যবইয়ে মডেলটি সাধারণত এভাবে বলা হয়: প্রথম মাসে একটি সদ্যোজাত জোড়া; প্রতিটি জোড়া জন্মের দ্বিতীয় মাস থেকে প্রতি মাসে একটি নতুন জোড়ার জন্ম দেয়; কোনো খরগোশ মরে না। Rn = n-তম মাসে জোড়ার সংখ্যা।
- n-তম মাসের জোড়া দুই দলে পড়ে: আগের মাসেও যারা ছিল (Rn−1টি), আর এই মাসে যারা জন্মাল।
- এই মাসে জন্ম দেয় ঠিক সেই জোড়াগুলো, যারা অন্তত দুই মাস আগে থেকে আছে, অর্থাৎ Rn−2টি।
- তাই Rn = Rn−1 + Rn−2 (n ≥ 3), R1 = R2 = 1; বারো মাসে R12 = 144.
মডেলটি জীববিজ্ঞান হিসেবে অবাস্তব: খরগোশ মরে, একবারে একাধিক বাচ্চা হয়। কিন্তু গণিতের দিক থেকে এর অনুমানগুলো স্পষ্ট, আর অনুমান স্পষ্ট বলেই কোথায় বাস্তবের সঙ্গে মেলে না, তা দেখানো যায়। ভালো মডেলের এটিই প্রথম গুণ। ইতিহাসের একটি সতর্কতা: ধারাটি এর আগেই ভারতীয় ছন্দশাস্ত্রে দেখা যায় বলে ইতিহাসবিদরা উল্লেখ করেন, Virahanka, Gopala আর Hemachandra-র নামের সঙ্গে; সঠিক তারিখ নিয়ে সূত্রভেদ আছে, পর্ব ১২-এর আলোচনাটি দেখুন।
ছন্দের প্রশ্নটি এই: ছোট মাত্রা (দৈর্ঘ্য 1) আর বড় মাত্রা (দৈর্ঘ্য 2) দিয়ে মোট n মাত্রার কয়টি ছন্দ বানানো যায়? শেষ মাত্রাটি ছোট হলে বাকি n − 1, বড় হলে বাকি n − 2; তাই mn = mn−1 + mn−2, m1 = 1, m2 = 2. এটি আসলে n-কে 1 আর 2-এর ক্রমযুক্ত যোগফলে লেখার সংখ্যা, যাকে পর্ব ৩৭ “গণনার কৌশল — lattice path, bijection আর double counting” composition বলেছে। সেকশন ১-এর সিঁড়ির ধাঁধাও হুবহু এটি।
পর্ব ৩৭-এ আপনি প্রমাণ দেখেছেন যে দৈর্ঘ্য n-এর যে bit string-এ পরপর দুটি 0 (“00”) নেই, তার সংখ্যা an = an−1 + an−2, a1 = 2, a2 = 3, অর্থাৎ an = Fn+2: শেষ bit 1 হলে আগের অংশ যেকোনো বৈধ string, শেষ bit 0 হলে তার আগে অবশ্যই 1. সেই প্রমাণ আবার করছি না; দুটি নতুন দিক দেখি।
- “11”-হীন string: প্রতিটি bit উল্টে দিলে (0 ↔ 1) “00”-হীন string ঠিক “11”-হীন string হয়ে যায়, আর উল্টানো একটি bijection (পর্ব ১৬)। তাই গণনাও একই, কোনো নতুন recurrence লাগে না। বাজারঘরের ভাষায়: যদি নিয়ম হয় রাইডার পরপর দুই দিন রাতের শিফটে থাকবেন না, সূচির সংখ্যা আগের মতোই।
- বড় নিষেধ, বড় order: “000” নিষিদ্ধ হলে শেষে থাকতে পারে 1, 10 বা 100; তিন দল, তাই an = an−1 + an−2 + an−3, order 3, আর তিনটি initial condition লাগে। অনুশীলন ১-এ এর প্রমাণ আছে।
লক্ষ করুন, খরগোশ, ছন্দ, সিঁড়ি আর bit string, চারটি ভিন্ন গল্প একই recurrence দেয়, শুধু initial condition আর সূচক একটু সরে যায়: Rn = Fn, mn = Fn+1, an = Fn+2. উপপাদ্য ১.২ ঠিক এই কথাটিই নিশ্চিত করে: recurrence আর initial condition মিললে ধারা মিলবেই, তাই দুটি গল্পের সম্পর্ক প্রমাণ করতে প্রতিটি পদ মেলানোর দরকার নেই।
৪ · Tower of Hanoi — recurrence, unrolling আর সর্বনিম্নতার প্রমাণ
ফরাসি গণিতবিদ Édouard Lucas ১৮৮৩ সালে একটি খেলনা-ধাঁধা বাজারে আনেন বলে জানা যায়, যার নাম Tower of Hanoi. তিনটি খুঁটি A, B, C; A-তে n-টি চাকতি, বড়টি নিচে, ছোটটি ওপরে। নিয়ম: একবারে একটি চাকতি সরানো যায়, শুধু কোনো খুঁটির সবচেয়ে ওপরেরটি, আর কখনো বড় চাকতি ছোটটির ওপর রাখা যায় না। সব চাকতি C-তে নিতে সবচেয়ে কম কয়টি চাল লাগে?
মডেল। সবচেয়ে বড় চাকতিটি (n নম্বর) C-তে নিতে হলে তার ওপরের n − 1টি চাকতি আগে সরাতে হবে, আর C-তেও রাখা যাবে না, তাই সেগুলো যাবে B-তে। কৌশলটি: (১) ওপরের n − 1টি A থেকে B-তে, C-কে সহায়ক ধরে; (২) বড় চাকতিটি A থেকে C-তে, এক চাল; (৩) n − 1টি B থেকে C-তে। H(n) = এই কৌশলে চালের সংখ্যা হলে
H(0) = 0, H(n) = 2H(n − 1) + 1 (n ≥ 1)
H(1) = 1, H(2) = 3, H(3) = 7, H(4) = 15. প্যাটার্নটি 2n − 1-এর মতো দেখায়। Unrolling দিয়ে অনুমানটি কোথা থেকে আসে, নিচের stepper ধাপে ধাপে দেখায়।
1H(n) = 2H(n−1) + 12= 4H(n−2) + 2 + 13= 8H(n−3) + 4 + 2 + 14= 2ᵏH(n−k) + 2ᵏ − 15k = n: 2ⁿ · 0 + 2ⁿ − 16প্রমাণ: induction
| বসানো | |
| সাধারণ রূপ | |
| n = 5-এ |
- শুরু। H(n) = 2H(n − 1) + 1. n = 5-এ এটি H(5) = 2H(4) + 1; ডান পাশে এখনো একটি অজানা H আছে।
- একবার বসানো। H(n − 1) = 2H(n − 2) + 1 বসালে H(n) = 2[2H(n − 2) + 1] + 1 = 4H(n − 2) + 2 + 1. n = 5-এ 4H(3) + 3.
- আবার। H(n − 2) = 2H(n − 3) + 1 বসালে 8H(n − 3) + 4 + 2 + 1. সামনের গুণক প্রতিবার দ্বিগুণ, আর পেছনে 2-এর ঘাতের একটি যোগফল জমছে। n = 5-এ 8H(2) + 7.
- প্যাটার্ন। k বার বসানোর পর H(n) = 2kH(n − k) + (2k−1 + … + 2 + 1) = 2kH(n − k) + 2k − 1, geometric series থেকে। n = 5, k = 4-এ 16H(1) + 15.
- ভিত্তিতে পৌঁছানো। k = n নিলে H(n − k) = H(0) = 0, তাই H(n) = 2n − 1. n = 5-এ 32 · 0 + 31 = 31. কিন্তু ধাপ ৪-এর “…” একটি অনুমান, প্রমাণ নয়।
- Induction দিয়ে নিশ্চিত করা। ভিত্তি H(0) = 0 = 20 − 1. H(k) = 2k − 1 ধরলে H(k + 1) = 2(2k − 1) + 1 = 2k+1 − 1. ∎ মানগুলো 1, 3, 7, 15, 31 হাতের গণনার সঙ্গে মেলে।
এখনো একটি বড় ফাঁক আছে। আমরা দেখালাম একটি কৌশল 2n − 1 চালে কাজ সারে। কিন্তু হয়তো আরও চতুর কোনো কৌশল আছে? “সবচেয়ে কম” দাবি করতে একটি lower bound লাগে: প্রমাণ করতে হবে যে যেকোনো বৈধ সমাধানে অন্তত এতগুলো চাল লাগে।
M(n) = n-টি চাকতির পুরো স্তূপ এক খুঁটি থেকে অন্য খুঁটিতে নেওয়ার সর্বনিম্ন চাল-সংখ্যা। তাহলে প্রতিটি n ≥ 0-এ M(n) = 2n − 1.
- ওপরের সীমা: ওপরের কৌশল H(n) = 2n − 1 চালে কাজ করে, তাই M(n) ≤ 2n − 1. — stepper
- নিচের সীমা, induction: M(0) = 0. ধরুন n ≥ 1, আর M(n − 1) ≥ 2n−1 − 1. যেকোনো একটি বৈধ সমাধান নিন। — induction
- বড় চাকতিটি অন্তত একবার সরবেই। তার প্রথম চালের মুহূর্তে সে নিজের খুঁটির সবচেয়ে ওপরে, আর যে খুঁটিতে যাচ্ছে সেটি খালি (নইলে ছোট চাকতির ওপর বড়টি পড়ত)। তাই বাকি n − 1টি চাকতি তখন তৃতীয় খুঁটিতে একটি স্তূপ হয়ে আছে। — নিয়ম
- শুরুতে সেই n − 1টি ছিল A-তে। বড় চাকতিটি তাদের কোনো চালে বাধা দেয় না (যেকোনো ছোট চাকতি তার ওপর বসতে পারে), তাই প্রথম চালের আগের ছোট চাকতির চালগুলো নিজেরাই n − 1 চাকতির স্তূপ সরানোর একটি বৈধ সমাধান: অন্তত M(n − 1)টি চাল। — অনুমান
- একইভাবে বড় চাকতির শেষ চালের মুহূর্তে বাকি n − 1টি একটি অন্য খুঁটিতে স্তূপ হয়ে আছে, আর সেখান থেকে C-তে বড়টির ওপর আসতে আবার অন্তত M(n − 1)টি চাল। — একই যুক্তি
- ধাপ ৪-এর চালগুলো প্রথম বড়-চালের আগে, ধাপ ৫-এর গুলো শেষ বড়-চালের পরে, তাই তারা আলাদা; সঙ্গে বড়টির অন্তত একটি চাল। মোট ≥ 2M(n − 1) + 1 ≥ 2(2n−1 − 1) + 1 = 2n − 1. — যোগের নিয়ম
- দুই সীমা মিলিয়ে M(n) = 2n − 1. ∎ — ধাপ ১, ৬
ধাপ ৬-এর যুক্তি শুধু সংখ্যা দেয় না, কাঠামোও দেয়: সর্বনিম্ন সমাধানে বড় চাকতি ঠিক একবার সরে, আর সমাধানটি আসলে একটিই। Lucas-এর বিপণনে একটি “৬৪টি সোনার চাকতির মন্দির” গল্প ছিল বলে জানা যায়, যা বানানো কিংবদন্তি, কোনো বাস্তব প্রথা নয়। 64টি চাকতিতে 264 − 1 = 18446744073709551615টি চাল; সেকেন্ডে একটি চালে প্রায় 5.85 × 1011 বছর। Hanoi-র recursive কোড আর call stack DSA কোর্সের পর্ব ০৭-এ আছে; এখানে আমাদের বিষয় সংখ্যাটির প্রমাণ।
৫ · Domino দিয়ে টালি — 2 × n আর 3 × n
বাজারঘরের ডেলিভারি ভ্যানের মেঝে 2 × n ঘরের একটি লম্বা পাটাতন, যাকে 1 × 2 মাপের একই রকম বাক্স দিয়ে পুরো ঢাকতে হবে, কোনো ফাঁক বা ওপরে-ওপরে না রেখে। প্রতিটি বাক্স খাড়া (দুই সারি জুড়ে) বা শোয়া (এক সারিতে দুই ঘর) বসতে পারে। কয়টি ভিন্ন বিন্যাস সম্ভব? এ ধরনের ঢাকাকে বলে domino tiling. নিচের ছবি মূল যুক্তিটি দেখায়।
2 × n বোর্ডের বাঁ কলাম দেখে দুই ক্ষেত্র
ওপরে: বাঁ কলামে একটি খাড়া domino, বাকি 2 × (n − 1). নিচে: বাঁ-ওপরের ঘরে শোয়া domino বসলে নিচের ঘরেও শোয়া domino বাধ্য, বাকি 2 × (n − 2).
1t(1) = 1, t(2) = 22বাঁ-ওপরের ঘর: খাড়া3বাঁ-ওপরের ঘর: শোয়া4t(n) = t(n−1) + t(n−2)5t(3), t(4), t(5)6t(n) = F(n+1)
| ক্ষেত্র | |
| বাকি বোর্ড | |
| গোনা |
- ছোট ক্ষেত্র। 2 × 1 বোর্ডে শুধু একটি খাড়া domino, তাই t(1) = 1. 2 × 2 বোর্ডে দুটি খাড়া পাশাপাশি, বা দুটি শোয়া একটির নিচে আরেকটি, তাই t(2) = 2.
- ক্ষেত্র ১। n ≥ 3. বাঁ-ওপরের ঘরটি যে domino ঢাকে, তা খাড়া হলে পুরো বাঁ কলাম ভরে যায়। বাকিটা একটি 2 × (n − 1) বোর্ড, যাকে t(n − 1) উপায়ে ঢাকা যায়।
- ক্ষেত্র ২। বাঁ-ওপরের ঘরের domino শোয়া হলে বাঁ-নিচের ঘরটি খাড়া domino পাবে না (ওপরের ঘর ভরা), তাই সেটিও শোয়া, ঠিক নিচে। বাকিটা 2 × (n − 2): t(n − 2) উপায়।
- যোগের নিয়ম। প্রতিটি বিন্যাস ঠিক একটি ক্ষেত্রে পড়ে, কারণ বাঁ-ওপরের ঘরের domino খাড়া বা শোয়া, দুটি একসঙ্গে নয়। তাই n ≥ 3-এ t(n) = t(n − 1) + t(n − 2).
- হিসাব। t(3) = 2 + 1 = 3, t(4) = 3 + 2 = 5, t(5) = 5 + 3 = 8. হাতে আঁকলে 2 × 4 বোর্ডে ঠিক ৫টি বিন্যাস পাবেন।
- সূত্র। t(1) = 1 = F2, t(2) = 2 = F3, আর recurrence এক; উপপাদ্য ১.২ বলে t(n) = Fn+1. যেমন t(10) = F11 = 89.
খাড়া domino = দৈর্ঘ্য 1-এর একটি অংশ, শোয়া জোড়া = দৈর্ঘ্য 2-এর অংশ ধরলে প্রতিটি টালি-বিন্যাস n-কে 1 আর 2-এর ক্রমযুক্ত যোগফলে লেখার একটি উপায়: সেকশন ৩-এর ছন্দের সঙ্গে একটি bijection. একই উত্তর তাই আশ্চর্য নয়।
3 × n বোর্ড, সংক্ষেপে। এখানে বাঁ কলামের তিনটি ঘর ঢাকার উপায় বেশি, আর একটি নতুন জিনিস আসে: n বিজোড় হলে ঘর 3n বিজোড়, অথচ প্রতিটি domino দুটি ঘর ঢাকে, তাই কোনো টালি নেই। জোড় n-এ শেষ কলাম ধরে একটু বেশি ক্ষেত্রভাগ করলে (একটি সহায়ক ধারা নিয়ে, যা এক কোণ খালি বোর্ড গোনে) পাওয়া যায় u(0) = 1, u(2) = 3, আর জোড় n ≥ 4-এ u(n) = 4u(n − 2) − u(n − 4). প্রমাণটি এখানে বাদ রাখছি; নিচের brute force সংখ্যাগুলো মিলিয়ে দেখায়, আর এ ধরনের recurrence কীভাবে সমাধান হয় তা পর্ব ৪৫ “Linear recurrence সমাধান — characteristic equation”-এ আসবে।
def count_tilings(rows, cols):
"""Brute force: cover the first empty cell, try both dominoes."""
grid = [[False] * cols for _ in range(rows)]
def go():
for c in range(cols):
for r in range(rows):
if grid[r][c]:
continue
total = 0
if r + 1 < rows and not grid[r + 1][c]: # vertical
grid[r][c] = grid[r + 1][c] = True
total += go()
grid[r][c] = grid[r + 1][c] = False
if c + 1 < cols and not grid[r][c + 1]: # horizontal
grid[r][c] = grid[r][c + 1] = True
total += go()
grid[r][c] = grid[r][c + 1] = False
return total
return 1 # board is full
return go()
t = {1: 1, 2: 2}
for n in range(3, 13):
t[n] = t[n - 1] + t[n - 2] # 2 x n
u = {0: 1, 2: 3}
for n in range(4, 13, 2):
u[n] = 4 * u[n - 2] - u[n - 4] # 3 x n, n even
print(" n 2xn brute recur | 3xn brute recur")
for n in range(1, 13):
print(f"{n:2} {count_tilings(2, n):9} {t[n]:5} |"
f" {count_tilings(3, n):9} {u.get(n, 0):5}")আসল আউটপুট (ব্রাউজারের Python):
n 2xn brute recur | 3xn brute recur
1 1 1 | 0 0
2 2 2 | 3 3
3 3 3 | 0 0
4 5 5 | 11 11
5 8 8 | 0 0
6 13 13 | 41 41
7 21 21 | 0 0
8 34 34 | 153 153
9 55 55 | 0 0
10 89 89 | 571 571
11 144 144 | 0 0
12 233 233 | 2131 2131
Brute force কোনো recurrence জানে না: সে বোর্ডের প্রথম খালি ঘর খোঁজে (কলাম ধরে, ওপর থেকে নিচে), সেখানে খাড়া আর শোয়া দুটোই চেষ্টা করে, আর বোর্ড ভরলে 1 গোনে। তবু দুই কলাম প্রতিটি সারিতে মেলে। 2 × n-এ Fibonacci, আর 3 × n-এ বিজোড় n-এ 0, জোড় n-এ 3, 11, 41, 153, 571, 2131. Brute force-এর খরচ বিন্যাসের সংখ্যার সঙ্গে বাড়ে, যা exponential; 3 × 30 বোর্ডে উত্তর 299303201, প্রায় ৩০ কোটি (recurrence থেকে scratch-এ হিসাব করা), যা একটি একটি করে গোনা ব্রাউজারে অবাস্তব; তখন শুধু recurrence-ই কাজের।
৬ · সমতলে রেখা আর Catalan-এর recurrence
একটি কাগজে n-টি সরলরেখা আঁকলে কাগজটি (অসীম সমতল ধরুন) সর্বোচ্চ কয়টি অংশে ভাগ হয়? একটি রেখায় ২ ভাগ, দুটিতে ৪ ভাগ। এখানে থেমে “প্রতিবার দ্বিগুণ, তাই 2n” বলে ফেলা সহজ, আর ভুল: তিনটি রেখায় সর্বোচ্চ ৭ ভাগ, ৮ নয় (নিচের ছবি)। “সর্বোচ্চ” পেতে রেখাগুলো general position-এ থাকতে হয়: কোনো দুটি সমান্তরাল নয়, আর কোনো তিনটি এক বিন্দু দিয়ে যায় না।
General position-এ ৩টি রেখা: ৭টি অংশ
L(n) = general position-এ n-টি রেখা সমতলকে যত অংশে ভাগ করে। তাহলে L(0) = 1, n ≥ 1-এ L(n) = L(n − 1) + n, আর closed form L(n) = n(n + 1)/2 + 1.
- প্রথম n − 1টি রেখা আঁকা আছে, অংশ L(n − 1)টি। n-তম রেখাটি কোনোটির সমান্তরাল নয়, তাই প্রত্যেকটিকে ঠিক একবার কাটে: n − 1টি ছেদবিন্দু। — সমান্তরাল নয়
- কোনো তিনটি রেখা এক বিন্দুতে মেলে না, তাই এই n − 1টি বিন্দু আলাদা। তারা নতুন রেখাকে n-টি টুকরোতে ভাগ করে: দুই প্রান্তে দুটি অসীম রশ্মি আর মাঝে n − 2টি রেখাংশ। — এক বিন্দুতে তিনটি নয়
- প্রতিটি টুকরো পুরোনো একটি অংশের ভেতর দিয়ে এক প্রান্ত থেকে আরেক প্রান্তে যায়, আর সেই অংশটিকে দুই ভাগ করে। তাই ঠিক n-টি নতুন অংশ যোগ হয়: L(n) = L(n − 1) + n. — গোনা
- Unrolling: L(n) = 1 + (1 + 2 + … + n) = 1 + n(n + 1)/2, Gauss-এর যোগফল (পর্ব ১৭); induction-এ যাচাই সরাসরি: 1 + (n − 1)n/2 + n = 1 + n(n + 1)/2. ∎ — পর্ব ১৭, ১০
সূত্রটিকে 1 + n + C(n, 2) হিসেবেও লেখা যায়: প্রথম 1 শুরুর অংশ, প্রতিটি রেখা একটি নতুন অংশ শুরু করে (n), আর প্রতিটি ছেদবিন্দু একটি করে (C(n, 2)টি বিন্দু)। এই দ্বিতীয় দৃষ্টিভঙ্গি পরে গ্রাফের Euler সূত্রে কাজে আসবে। নিচের ব্লক Hanoi আর রেখার দুই recurrence-কে তাদের closed form-এর সঙ্গে মেলায়, আর Hanoi-র আসল চালগুলোও তৈরি করে গোনে।
from math import comb
def hanoi(n, src="A", via="B", dst="C", moves=None):
if moves is None:
moves = []
if n > 0:
hanoi(n - 1, src, dst, via, moves) # n-1 disks out of the way
moves.append((n, src, dst)) # the largest disk, once
hanoi(n - 1, via, src, dst, moves) # n-1 disks back on top
return moves
print("3 disks:", " ".join(f"{d}:{a}>{b}" for d, a, b in hanoi(3)))
H = [0] # H(0) = 0 moves
L = [1] # L(0) = 1 region
for n in range(1, 21):
H.append(2 * H[-1] + 1) # H(n) = 2H(n-1) + 1
L.append(L[-1] + n) # L(n) = L(n-1) + n
print(" n H(n) moves 2^n-1 | L(n) n(n+1)/2+1 1+n+C(n,2)")
for n in (1, 2, 3, 5, 8, 10):
print(f"{n:2} {H[n]:5} {len(hanoi(n)):5} {2**n - 1:6} |"
f" {L[n]:4} {n*(n+1)//2 + 1:9} {1 + n + comb(n, 2):9}")
print("n <= 20 all agree:",
all(H[n] == 2**n - 1 and L[n] == n*(n+1)//2 + 1 for n in range(21)))
print("H(64) =", 2**64 - 1)আসল আউটপুট (ব্রাউজারের Python):
3 disks: 1:A>C 2:A>B 1:C>B 3:A>C 1:B>A 2:B>C 1:A>C
n H(n) moves 2^n-1 | L(n) n(n+1)/2+1 1+n+C(n,2)
1 1 1 1 | 2 2 2
2 3 3 3 | 4 4 4
3 7 7 7 | 7 7 7
5 31 31 31 | 16 16 16
8 255 255 255 | 37 37 37
10 1023 1023 1023 | 56 56 56
n <= 20 all agree: True
H(64) = 18446744073709551615
প্রথম লাইনে ৩টি চাকতির সাতটি চাল: 3:A>C মানে ৩ নম্বর (সবচেয়ে বড়) চাকতি A থেকে C-তে। বড় চাকতিটি ঠিক মাঝখানে একবারই সরে, আর তার আগে-পরে তিনটি করে চাল, উপপাদ্য ৪.১-এর কাঠামো হুবহু। টেবিলে recurrence, আসলে তৈরি করা চালের সংখ্যা আর closed form তিনটি একই। রেখার দিকে দুটি closed form-ও মেলে। “n ≤ 20 সব মেলে” একটি পরীক্ষা, প্রমাণ নয়: প্রমাণ উপরের induction. Python-এর int যত বড় দরকার তত বড় হয়, তাই 264 − 1 নির্ভুল ছাপা হলো।
Catalan-এর recurrence — binary tree ভাগ করে
বাজারঘরের সার্চ ইঞ্জিন পণ্যের id একটি binary tree-এ রাখে: প্রতিটি node-এর একটি বাঁ আর একটি ডান subtree, যেকোনোটি খালি হতে পারে। n-টি node-এর কতগুলো ভিন্ন আকারের binary tree আছে? পর্ব ৩৭-এ reflection দিয়ে প্রমাণ হয়েছে উত্তরটি Catalan সংখ্যা Cn = C(2n, n)/(n + 1). এখানে recurrence-এর পথে একই সংখ্যায় পৌঁছাই।
Cn = n-টি node-এর binary tree-এর সংখ্যা (খালি tree-ও একটি tree হিসেবে গোনা হয়, তাই C0 = 1). তাহলে প্রতিটি n ≥ 0-এ
Cn+1 = C0Cn + C1Cn−1 + … + CnC0 = ∑i=0n CiCn−i
- n + 1টি node-এর tree-তে একটি root, আর বাকি n-টি বাঁ আর ডান subtree-তে ভাগ হয়। বাঁ দিকে i-টি হলে ডানে n − i-টি, যেখানে 0 ≤ i ≤ n. — গঠন
- i ঠিক করলে বাঁ subtree Ci উপায়ে আর ডানটি স্বাধীনভাবে Cn−i উপায়ে বাছা যায়, আর ভিন্ন বাছাই ভিন্ন tree দেয়: CiCn−iটি। — গুণের নিয়ম
- ভিন্ন i-এর tree-গুলো আলাদা (বাঁ subtree-এর আকার ভিন্ন), তাই সব i যোগ করলে Cn+1 = ∑ CiCn−i. ∎ — যোগের নিয়ম
এখান থেকে C1 = 1, C2 = 2, C3 = 5, C4 = 14, C5 = 42, যা C(2n, n)/(n + 1)-এর সঙ্গে মেলে (scratch-এ n ≤ 10 পর্যন্ত মেলানো)। এই recurrence-এর order নির্দিষ্ট নয়: Cn+1 আগের সব পদের ওপর নির্ভর করে। তাই এটি “full history” recurrence, আর তবু উপপাদ্য ১.২-এর একই strong induction যুক্তিতে একটি initial condition C0 = 1 থেকেই পুরো ধারা অনন্যভাবে ঠিক হয়। এর generating function দিয়ে closed form পাওয়ার কৌশল পর্ব ৪৮ “Generating function — গণনাকে বীজগণিতে রূপান্তর”-এ আসবে।
৭ · Josephus সমস্যা — binary-তে লুকানো উত্তর
বাজারঘরের অফিসে n জন কর্মী বৃত্তাকারে বসে, নম্বর 1 থেকে n. ঈদের উপহার-লটারির একটি মজার নিয়ম: 1 নম্বর থেকে গোনা শুরু, প্রথম জন থাকেন, পরের জন বাদ যান, তার পরের জন থাকেন, তার পরের জন বাদ, এভাবে বৃত্তে ঘুরে ঘুরে প্রতি দ্বিতীয় জন বাদ যান। শেষে যিনি একা থাকেন, তিনি বড় উপহারটি পান। কোন নম্বরে বসলে জেতা যায়? এটি Josephus সমস্যা; বিজয়ীর নম্বর J(n).
1বৃত্তে 1 … 102বাদ: 2, 43বাদ: 6, 84বাদ: 10, চক্র শেষ5বাদ: 3, 76বাদ: 17বাদ: 9, বিজয়ী 58যাচাই: 1010 → 0101
| বাকি | |
| বাদের ক্রম | |
| পরে থাকবেন |
- শুরু। ১০ জন বৃত্তে, 1 থেকে গোনা। নিয়ম: একজন থাকেন, পরের জন বাদ। 10-এর পরে আবার 1 আসে।
- প্রথম দুই বাদ। 1 থাকেন, 2 বাদ; 3 থাকেন, 4 বাদ। এরপর পালা 5-এর, তিনি থাকবেন।
- আরও দুই। 5 থাকেন, 6 বাদ; 7 থাকেন, 8 বাদ। প্রথম চক্রে সব জোড় নম্বর একে একে যাচ্ছেন।
- প্রথম চক্র শেষ। 9 থাকেন, 10 বাদ। বাকি 1, 3, 5, 7, 9: পাঁচজন, আর পরের পালা আবার 1-এর, যিনি থাকবেন। অবস্থাটি ঠিক ৫ জনের একটি নতুন Josephus খেলা, শুধু নম্বরগুলো ভিন্ন।
- দ্বিতীয় চক্র। 1 থাকেন, 3 বাদ; 5 থাকেন, 7 বাদ। বাকি 1, 5, 9, আর পালা 9-এর, তিনি থাকবেন।
- তৃতীয় চক্র। 9 থাকেন, 1 বাদ। বাকি 5 আর 9; পালা 5-এর, তিনি থাকবেন।
- শেষ। 5 থাকেন, 9 বাদ। বিজয়ী 5. পুরো বাদের ক্রম 2, 4, 6, 8, 10, 3, 7, 1, 9.
- দুই সূত্রে যাচাই। ধাপ ৪-এর পর্যবেক্ষণ: ৫ জনের খেলার k-তম জন আসলে 2k − 1 নম্বর। ৫ জনের খেলায় বিজয়ী 3 (5 = 4 + 1), তাই J(10) = 2 · 3 − 1 = 5. Binary-তে 10 = 1010; প্রথম 1-টি শেষে সরালে 0101 = 5.
J(1) = 1, আর n ≥ 1-এ J(2n) = 2J(n) − 1, J(2n + 1) = 2J(n) + 1.
- জোড় 2n জন: প্রথম চক্রে 2, 4, …, 2n বাদ যান, আর পালা আবার 1-এর। বাকিরা 1, 3, …, 2n − 1: n জনের একটি নতুন খেলা, যার k-তম আসন আসলে 2k − 1 নম্বর। সেই খেলার বিজয়ী J(n)-তম আসন, তাই J(2n) = 2J(n) − 1. — নতুন নামকরণ
- বিজোড় 2n + 1 জন: 2, 4, …, 2n বাদের পর পালা 2n + 1-এর, তিনি থাকেন, আর 1 বাদ যান। বাকিরা 3, 5, …, 2n + 1, শুরু 3 থেকে: k-তম আসন 2k + 1 নম্বর। তাই J(2n + 1) = 2J(n) + 1. ∎ — একই যুক্তি
এটি একটি divide-and-conquer ধরনের recurrence: n থেকে সরাসরি প্রায় n/2-তে নামে, তাই J(n) হিসাবে মাত্র প্রায় log2 n ধাপ লাগে। ছোট মানগুলো লিখলে একটি প্যাটার্ন চোখে পড়ে: J(1..8) = 1; 1, 3; 1, 3, 5, 7; 1. প্রতিটি 2-এর ঘাতে আবার 1, তারপর 2 করে বাড়ে।
n = 2m + l, যেখানে m ≥ 0 আর 0 ≤ l < 2m. তাহলে J(n) = 2l + 1.
- m-এর ওপর induction. ভিত্তি m = 0: n = 1, l = 0, আর J(1) = 1 = 2 · 0 + 1. — ভিত্তি
- অনুমান: m ≥ 1, আর 2m−1 + l′ আকারের প্রতিটি সংখ্যায় (0 ≤ l′ < 2m−1) সূত্রটি সত্য। — inductive hypothesis
- l জোড়: n = 2(2m−1 + l/2), আর l/2 < 2m−1. উপপাদ্য ৭.১-এ J(n) = 2J(2m−1 + l/2) − 1 = 2(2 · l/2 + 1) − 1 = 2l + 1. — J(2n) নিয়ম
- l বিজোড়: n = 2(2m−1 + (l − 1)/2) + 1, তাই J(n) = 2(2 · (l − 1)/2 + 1) + 1 = 2(l − 1) + 2 + 1 = 2l + 1. ∎ — J(2n + 1) নিয়ম
Binary-র চমক। n = 2m + l-এর binary রূপ হলো একটি 1, তারপর l-এর m-bit রূপ। 2l + 1 মানে l-কে এক ঘর বাঁয়ে সরিয়ে শেষে 1 বসানো। অর্থাৎ J(n) পেতে n-এর binary-র প্রথম 1-টি তুলে শেষে বসান: 100 = 1100100 → 1001001 = 73. নিচের ব্লক simulation, recurrence আর binary-র তিন পথ মেলায়।
def josephus_sim(n):
"""People 1..n in a circle; every second person leaves."""
people = list(range(1, n + 1))
i, order = 0, []
while len(people) > 1:
i = (i + 1) % len(people) # skip one, remove the next
order.append(people.pop(i))
return people[0], order
def josephus_rec(n): # J(1)=1, J(2m)=2J(m)-1, J(2m+1)=2J(m)+1
if n == 1:
return 1
j = josephus_rec(n // 2)
return 2 * j - 1 if n % 2 == 0 else 2 * j + 1
def josephus_bits(n): # move the leading 1 to the end
b = bin(n)[2:]
return int(b[1:] + b[0], 2)
survivor, order = josephus_sim(10)
print("n = 10 order:", order, "survivor:", survivor)
for n in (10, 41, 100, 1000, 2026):
m = n.bit_length() - 1 # n = 2^m + l
l = n - 2**m
print(f"n={n:4} sim={josephus_sim(n)[0]:4} rec={josephus_rec(n):4}"
f" 2l+1={2*l + 1:4} {bin(n)[2:]} -> {bin(josephus_bits(n))[2:]}")
print("n <= 500 all agree:", all(
josephus_sim(n)[0] == josephus_rec(n) == josephus_bits(n)
for n in range(1, 501)))আসল আউটপুট (ব্রাউজারের Python):
n = 10 order: [2, 4, 6, 8, 10, 3, 7, 1, 9] survivor: 5
n= 10 sim= 5 rec= 5 2l+1= 5 1010 -> 101
n= 41 sim= 19 rec= 19 2l+1= 19 101001 -> 10011
n= 100 sim= 73 rec= 73 2l+1= 73 1100100 -> 1001001
n=1000 sim= 977 rec= 977 2l+1= 977 1111101000 -> 1111010001
n=2026 sim=2005 rec=2005 2l+1=2005 11111101010 -> 11111010101
n <= 500 all agree: True
প্রথম লাইন stepper-এর বাদের ক্রম হুবহু দেয়। তারপর প্রতিটি n-এ তিন পথ একই উত্তর দেয়। Binary কলামে লক্ষ করুন: 1010 → 101, কারণ সামনে চলে আসা 0 Python ছাপে না: 0101 আর 101 একই সংখ্যা 5. Simulation-এ প্রতিটি pop তালিকা সরায়, তাই খরচ প্রায় n2; recurrence প্রায় log2 n ধাপে, আর binary সূত্র কার্যত এক ধাপে। মডেল থেকে closed form-এ যাওয়ার লাভ এটিই।
৮ · Unrolling-এর কৌশল, algorithm-এর recurrence আর পরীক্ষার অঙ্ক
এই পর্বে বারবার একই তিন ধাপ এসেছে: recurrence বানানো, unrolling করে closed form অনুমান, আর induction দিয়ে প্রমাণ। Unrolling-এর দুটি ছাঁচ মনে রাখার মতো, কারণ পরীক্ষায় বেশিরভাগ order 1-এর recurrence এদের একটিতে পড়ে:
| Recurrence | Unrolling-এর ফল | এই পর্বের উদাহরণ |
|---|---|---|
| an = an−1 + f(n) | a0 + f(1) + … + f(n) | রেখা: f(n) = n |
| an = c · an−1 + d, c ≠ 1 | cna0 + d(cn − 1)/(c − 1) | Hanoi (c = 2, d = 1), ঋণ (c = 1 + r, d = −P) |
| T(n) = T(n/2) + d, n = 2k | T(1) + dk = T(1) + d log2 n | binary search |
| T(n) = 2T(n/2) + n, n = 2k | nT(1) + n log2 n | merge sort |
Algorithm-এর recurrence, এক ঝলক। Binary search সাজানো তালিকার মাঝের উপাদান দেখে অর্ধেক ফেলে দেয়। n = 2k হলে তুলনার একটি সরল মডেল T(1) = 1, T(n) = T(n/2) + 1; unrolling-এ T(n) = log2 n + 1, যেমন T(1024) = 11. Merge sort তালিকা দুই ভাগ করে, প্রতিটি সাজায়, তারপর জোড়া দেয়। জোড়া দিতে বড়জোর n − 1টি তুলনা লাগে; সহজ রাখতে n ধরলে W(1) = 0, W(n) = 2W(n/2) + n, আর unrolling-এ প্রতিটি স্তরে মোট কাজ n, স্তর log2 n-টি, তাই W(n) = n log2 n; যেমন W(1024) = 10240 (scratch-এ যাচাই করা)। n যখন 2-এর ঘাত নয়, তখন ⌊n/2⌋ আর ⌈n/2⌉ আসে, আর কেন 2-এর ঘাত ধরে নেওয়া যায়, master theorem-এর পূর্ণ প্রমাণসহ, তা পর্ব ৪৭ “Divide-and-conquer recurrence আর master theorem-এর গণিত”-এর বিষয়। Algorithm বিশ্লেষণে এদের ব্যবহার দেখতে DSA কোর্সের পর্ব ০৮ আর পর্ব ০৯ পড়ুন।
অঙ্ক ১: Hanoi আর রেখা
(ক) ১০টি চাকতির Hanoi-তে সর্বনিম্ন চাল? (খ) general position-এ ১৫টি রেখা সমতলকে কয় ভাগ করে?
- (ক) M(10) = 210 − 1 = 1023, উপপাদ্য ৪.১ থেকে; এটি শুধু একটি কৌশলের চাল নয়, সর্বনিম্ন।
- (খ) L(15) = 15 · 16/2 + 1 = 121.
অঙ্ক ২: টালি আর Catalan
(ক) 2 × 12 বোর্ডে domino টালি কয়টি? (খ) recurrence দিয়ে C5 বের করুন।
- (ক) t(n) = Fn+1, তাই t(12) = F13 = 233, ব্লকের brute force-এর সঙ্গে মেলে।
- (খ) C5 = C0C4 + C1C3 + C2C2 + C3C1 + C4C0 = 14 + 5 + 4 + 5 + 14 = 42.
অঙ্ক ৩: Josephus
৪১ জনের বৃত্তে প্রতি দ্বিতীয় জন বাদ গেলে কে থাকেন?
- 41 = 32 + 9, তাই m = 5, l = 9.
- J(41) = 2 · 9 + 1 = 19. Binary-তে 101001 → 010011 = 19, ব্লকের আউটপুটের সঙ্গে মেলে।
যুক্তির ভুল ধরুন: “ভুল শুরুর মান”
একজন লিখলেন: “দৈর্ঘ্য n-এর ‘00’-হীন bit string-এর সংখ্যা an. শেষ bit দেখে an = an−1 + an−2. শুরু a1 = 1 (string ‘1’) আর a2 = 2 (‘01’, ‘11’), তাই a3 = 3, a4 = 5, a5 = 8.”
ভুল কোথায়: recurrence ঠিক, initial condition ভুল। দৈর্ঘ্য 1-এ ‘0’-ও বৈধ, কারণ তাতে পরপর দুটি 0 নেই, তাই a1 = 2; দৈর্ঘ্য 2-এ ‘10’-ও বৈধ, তাই a2 = 3. সঠিক মান a3 = 5 (010, 011, 101, 110, 111), a4 = 8, a5 = 13. উপপাদ্য ১.২ বলে recurrence একই থাকলেও ভুল শুরু পুরো ধারাকে ভুল করে, আর এখানে প্রতিটি পদ এক ধাপ পেছনের Fibonacci হয়ে গেছে। প্রতিকার: initial condition-গুলোও brute force-এ মিলিয়ে নিন; কৌশল ১.৩-এর ধাপ ২ এ জন্যই।
আরও গভীরে: চারটি খুঁটির Hanoi-তে সর্বনিম্ন চাল নিয়ে ১৯৪১ সালে Frame আর Stewart একটি কৌশল দেন, যা সর্বোত্তম বলে বহুদিন শুধু অনুমান ছিল; ২০১৪ সালে Thierry Bousch চার খুঁটির ক্ষেত্রে এটি প্রমাণ করেন বলে জানা যায়, আর পাঁচ বা বেশি খুঁটিতে প্রশ্নটি খোলা বলে ধরা হয়। m × n বোর্ডের domino টালির সংখ্যার একটি নির্ভুল গুণফল-সূত্র ১৯৬১ সালে Kasteleyn আর আলাদাভাবে Temperley ও Fisher দেন, যা পরিসংখ্যানিক পদার্থবিদ্যায় বড় ভূমিকা রাখে। আর প্রতি k-তম জন বাদের Josephus-এ J(1) = 1, J(n) = ((J(n − 1) + k − 1) mod n) + 1, যা n ধাপে চলে; k = 3, n = 41-এ উত্তর 31 (scratch-এ simulation-এ মেলানো)।
পরীক্ষা ও ইন্টারভিউতে: বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় এই ধরনের প্রশ্ন প্রায়ই আসে:
- এক বা দুই ধাপে n সিঁড়ি ওঠার উপায়? sn = sn−1 + sn−2, s1 = 1, s2 = 2, তাই Fn+1; ১০ সিঁড়িতে 89.
- Hanoi-র recurrence আর সমাধান? H(n) = 2H(n − 1) + 1, H(1) = 1, তাই 2n − 1; এটি সর্বনিম্নও।
- ৮টি রেখায় সমতলের সর্বোচ্চ অংশ? 8 · 9/2 + 1 = 37.
- Order 3-এর recurrence-এ কয়টি initial condition? ৩টি; কম দিলে অসীম সমাধান, বেশি দিলে বিরোধ হতে পারে।
- J(100), প্রতি দ্বিতীয় জন বাদ? 100 = 64 + 36, তাই 2 · 36 + 1 = 73.
৯ · AI-এর যুগে — recurrence মডেল যাচাই
“এই সমস্যার recurrence কী?” প্রশ্নে AI সহকারী প্রায় সবসময় একটি সুন্দর উত্তর দেয়। ঝুঁকি হলো উত্তরটি দেখতে ঠিক, কিন্তু একটি ক্ষেত্র বাদ পড়েছে বা শুরুর মান এক ঘর সরে আছে। Recurrence-এ ছোট ভুল প্রতিটি ধাপে বয়ে চলে, তাই শেষ সংখ্যা অনেক দূরে চলে যায়। এই বিষয়ে যে ভুলগুলো নিয়মিত দেখা যায়:
| AI-এর ভুল | কীভাবে ধরবেন |
|---|---|
| Recurrence ঠিক, কিন্তু initial condition ভুল বা সংখ্যায় কম | n = 0, 1, 2, 3-এ সব বস্তু itertools দিয়ে গুনে মেলান; order যত, initial condition তত কি না দেখুন |
| ক্ষেত্রভাগে একটি ক্ষেত্র বাদ বা দুই ক্ষেত্রে ওভারল্যাপ (double counting) | প্রতিটি বস্তু ঠিক একটি ক্ষেত্রে পড়ে কি না, তা এক বাক্যে যুক্তি দিয়ে বলুন |
| কয়েকটি মান দেখে closed form ঘোষণা, প্রমাণ ছাড়া (যেমন রেখায় 2n) | আরও একটি মান হিসাব করুন, তারপর induction প্রমাণ চান |
| “সর্বনিম্ন” দাবি করে শুধু একটি কৌশল দেখানো (Hanoi) | যেকোনো সমাধানের জন্য lower bound-এর যুক্তি আছে কি না দেখুন |
| শর্ত বাদ দেওয়া: general position, n = 2-এর ঘাত, সুদ মাসে না বছরে | প্রতিটি উপপাদ্যের অনুমান লিখতে বলুন; শর্ত ভেঙে একটি counterexample খুঁজুন |
| বড় সংখ্যা বা floating-point-এ গোলাকরণ (ঋণের হিসাব) | int বা fractions.Fraction দিয়ে আবার হিসাব করুন |
| ইতিহাস বানানো: Hanoi-র “প্রাচীন মন্দির”, ভুল সাল বা নাম | বিশ্বস্ত পাঠ্যবইয়ে মেলান; কিংবদন্তিকে ইতিহাস বলা হলো কি না দেখুন |
AI-কে recurrence মডেল বানাতে বললে এমন কাঠামো দিন, যাতে প্রতিটি ধাপ আলাদা করে যাচাই করা যায়:
AI-এর উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট
Recurrence-এর বড় সুবিধা হলো যাচাই সস্তা: কয়েক লাইনের brute force ছোট n-এ সব বস্তু গুনে ফেলে, আর recurrence-এর প্রথম দশ-বারোটি মান তার সঙ্গে মিললে মডেলে বড় ভুল থাকার সম্ভাবনা কমে যায়। কিন্তু মিল প্রমাণ নয়; প্রমাণ আসে ক্ষেত্রভাগের যুক্তি আর induction থেকে। দুটি একসঙ্গে রাখুন।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১একই Fibonacci ধারা মধ্যযুগের ইউরোপের খরগোশের ধাঁধায় আর তার আগের ভারতীয় ছন্দশাস্ত্রে দেখা গেল কেন? এটি কাকতালীয়, নাকি গণনার গঠনের মিল?
পিসার Leonardo ১২০২ সালে Liber Abaci লেখেন, আর ১২২৮ সালে একটি সংশোধিত সংস্করণ বের হয় বলে জানা যায়। বইটির মূল উদ্দেশ্য ছিল ইউরোপের বণিকদের হিন্দু-আরবি দশমিক সংখ্যা আর তা দিয়ে হিসাব শেখানো; খরগোশের ধাঁধা ছিল অনেক অনুশীলনের একটি। “Fibonacci” নামটিও পরে আসে, “Bonacci-র পুত্র” অর্থে, আর ধারাটির নাম Fibonacci রাখার কৃতিত্ব সাধারণত উনিশ শতকের Édouard Lucas-কে দেওয়া হয়, যিনি Hanoi-র ধাঁধারও স্রষ্টা।
অন্যদিকে প্রাচীন ও মধ্যযুগের ভারতীয় ছন্দশাস্ত্রে প্রশ্ন ছিল: লঘু (এক মাত্রা) আর গুরু (দুই মাত্রা) অক্ষর দিয়ে মোট n মাত্রার কয়টি ছন্দ-বিন্যাস সম্ভব? Pingala-র ছন্দসূত্রের সঙ্গে এর সূচনা যুক্ত করা হয়, আর Virahanka, Gopala ও Hemachandra এই সংখ্যাগুলো স্পষ্টভাবে হিসাব করেন বলে ইতিহাসবিদরা উল্লেখ করেন; তাদের সঠিক সময়কাল নিয়ে সূত্রভেদ আছে, তবে Hemachandra-কে সাধারণত দ্বাদশ শতকে ধরা হয়, Leonardo-র কিছু আগে।
এটি কাকতালীয় নয়। দুই সমস্যার গঠন একই: প্রতিটি বস্তু শেষ “এককটি” দিয়ে দুই দলে ভাগ হয়, একটি দলে আকার এক কমে, অন্যটিতে দুই। খরগোশে “এই মাসে নতুন” আর “আগেও ছিল”, ছন্দে “শেষ অক্ষর লঘু” আর “শেষ অক্ষর গুরু”, domino-তে “খাড়া” আর “শোয়া জোড়া”। যে কোনো সমস্যায় এই দুই-পথের ক্ষেত্রভাগ দেখা দিলে Fibonacci আসবেই, আর initial condition ঠিক করে দেয় কোন সূচক থেকে। এই কারণেই Fibonacci প্রকৃতিতে, algorithm-এ (Euclid-এর সবচেয়ে খারাপ ইনপুট, Lamé ১৮৪৪, পর্ব ১২) আর তথ্য-কাঠামোয় (Fibonacci heap, AVL tree-র উচ্চতার সীমা) বারবার দেখা যায়। প্রকৃতির কিছু “Fibonacci” দাবি অবশ্য অতিরঞ্জিত; সূর্যমুখীর বীজের সর্পিলে Fibonacci সংখ্যা প্রায়ই মেলে, কিন্তু সব ফুলে নয়, আর মানবদেহে golden ratio-র অনেক দাবি জনপ্রিয় মিথ।
মূল উপলব্ধি: একই recurrence দুই ভিন্ন জায়গায় এলে তার পেছনে প্রায়ই একই ক্ষেত্রভাগের গঠন থাকে; সেই গঠনটি খুঁজে পাওয়াই মডেল বানানোর আসল দক্ষতা।
প্র ০২Hanoi-তে 2n − 1 চালের একটি কৌশল দেখানোই কি যথেষ্ট ছিল না? Lower bound প্রমাণ কেন আলাদা কাজ, আর কম্পিউটার বিজ্ঞানে এর গুরুত্ব কী?
একটি কৌশল দেখালে প্রমাণ হয় “এত চালে করা যায়”, অর্থাৎ একটি ঊর্ধ্বসীমা। “এর কমে করা যায় না” একটি সম্পূর্ণ ভিন্ন দাবি, কারণ তা সব সম্ভাব্য কৌশলের কথা বলে, এমনকি যেগুলো এখনো কেউ ভাবেনি। Hanoi-তে উপপাদ্য ৪.১-এর যুক্তি সুন্দর, কারণ এটি কোনো কৌশলের বিবরণ ব্যবহার করে না: শুধু নিয়ম থেকে বলে যে বড় চাকতির প্রথম আর শেষ চালের মুহূর্তে বাকিরা একটি স্তূপে থাকতে বাধ্য। এই ধরনের যুক্তিকে প্রায়ই adversary বা “বাধ্যতামূলক অবস্থা” যুক্তি বলা হয়।
কম্পিউটার বিজ্ঞানে এই দুই সীমার পার্থক্য কেন্দ্রীয়। তুলনা-ভিত্তিক sorting-এ merge sort প্রায় n log2 n তুলনায় কাজ সারে, আর আলাদা একটি যুক্তি (decision tree-তে অন্তত n!টি পাতা থাকতে হয়, তাই উচ্চতা অন্তত log2(n!)) দেখায় যে কোনো তুলনা-ভিত্তিক algorithm এর চেয়ে অনেক ভালো করতে পারে না। এই দুই সীমা মিললে আমরা বলি algorithm-টি “সর্বোত্তম”। অনেক গুরুত্বপূর্ণ সমস্যায় এই ফাঁক আজও খোলা: matrix গুণের সেরা সূচক বা P বনাম NP প্রশ্ন আসলে lower bound-এর অভাবেরই গল্প। ঊর্ধ্বসীমা দেখাতে একটি চতুর কৌশল যথেষ্ট, কিন্তু নিচের সীমা প্রমাণ করা সাধারণত অনেক কঠিন।
Hanoi নিজেও এর উদাহরণ দেয়। তিন খুঁটিতে সীমা দুটি মেলে, তাই গল্প শেষ। চার খুঁটিতে Frame–Stewart কৌশল ১৯৪১ সাল থেকে জানা, কিন্তু তা সর্বোত্তম কি না প্রমাণ হতে প্রায় সত্তর বছর লেগেছে বলে জানা যায়, আর আরও খুঁটিতে প্রশ্নটি খোলা বলে ধরা হয়। একটি ছোট খেলনার প্রশ্নেও “নিচের সীমা” কতটা কঠিন হতে পারে, এটি তার ভালো উদাহরণ। Lucas-এর ৬৪ চাকতির মন্দির-কিংবদন্তি বিপণনের গল্প হলেও তার বার্তাটি গাণিতিকভাবে সত্য: 264 − 1 চাল সত্যিই এড়ানো যায় না।
মূল উপলব্ধি: “এভাবে করা যায়” আর “এর কমে করা যায় না” দুটি আলাদা উপপাদ্য; সর্বোত্তমতা দাবি করতে দুটিই প্রমাণ করতে হয়।
প্র ০৩Josephus সমস্যার নাম কোথা থেকে এল, আর একটি খেলার ধাঁধা কীভাবে binary সংখ্যা আর algorithm-এর সঙ্গে জুড়ে গেল?
নামটি প্রথম শতকের ইহুদি-রোমান ইতিহাসবিদ Flavius Josephus-এর নামে। তাঁর লেখা যুদ্ধের ইতিহাসে একটি অবরোধের ঘটনায় একদল সৈন্যের পালাক্রমে মৃত্যুর সিদ্ধান্তের বিবরণ আছে বলে জানা যায়, আর পরের শতাব্দীগুলোতে সেই গল্পটি থেকে “বৃত্তে বসে প্রতি k-তম জন বাদ” ধরনের গাণিতিক ধাঁধা তৈরি হয়। মূল গল্পের বিবরণ আর পরের সংস্করণগুলোর সংখ্যা (৪১ জন, প্রতি তৃতীয় জন, ইত্যাদি) এক নয়, তাই ইতিহাস হিসেবে এটি সাবধানে পড়া ভালো। আমাদের পর্বে আমরা গল্পটি একটি নিরীহ উপহার-লটারিতে বদলে নিয়েছি; গণিত একই থাকে।
গাণিতিক বিশ্লেষণ বহু শতাব্দী ধরে হয়েছে; আধুনিক পাঠ্যবইয়ে প্রতি দ্বিতীয় জনের সংস্করণটি সবচেয়ে বিখ্যাত হয় Graham, Knuth আর Patashnik-এর Concrete Mathematics বইয়ের প্রথম অধ্যায়ে, যেখানে Hanoi আর সমতলে রেখার সঙ্গে এটি recurrence-এর তিনটি প্রথম উদাহরণের একটি; এই পর্বের সাজানোও সেই ধারা অনুসরণ করে। সেখানে J(2n) আর J(2n + 1)-এর recurrence থেকে binary rotation-এর সূত্রে যাওয়ার গল্পটি একটি শিক্ষণীয় বিষয় হিসেবে দেখানো হয়: recurrence n-কে অর্ধেক করে, আর “অর্ধেক করা” binary-তে শেষ bit বাদ দেওয়া। তাই binary-তে উত্তর লুকিয়ে থাকা স্বাভাবিক।
কম্পিউটার বিজ্ঞানে Josephus প্রায়ই circular linked list বা queue অনুশীলনের সমস্যা হিসেবে আসে, আর সাধারণ k-এর জন্য J(n) = ((J(n − 1) + k − 1) mod n) + 1 recurrence একটি O(n) সমাধান দেয়, যেখানে simulation-এ O(nk) বা তালিকা সরানোর কারণে O(n2) লাগে। এই পর্বের ব্লক দেখিয়েছে, একই প্রশ্নের তিন সমাধানের খরচ n2, log n আর কার্যত ধ্রুবক। মডেল থেকে recurrence, recurrence থেকে closed form: প্রতিটি ধাপ গণনাকে সস্তা করে।
মূল উপলব্ধি: n-কে অর্ধেক করা recurrence-এর উত্তর প্রায়ই n-এর binary রূপে লেখা থাকে; Josephus তার সবচেয়ে পরিষ্কার উদাহরণ।
প্র ০৪ঋণের recurrence বাস্তবের কতটা কাছে? একটি গাণিতিক মডেল কখন বিশ্বাস করবেন, আর কোথায় সে ভেঙে পড়ে?
Bn = (1 + r)Bn−1 − P মডেলটি ধরে নেয়: সুদের হার পুরো সময় স্থির, সুদ মাসে একবার যোগ হয়, প্রতিটি কিস্তি সময়মতো আর সমান, আর কোনো ফি বা জরিমানা নেই। এই প্রতিটি অনুমান বাস্তবে ভাঙতে পারে। অনেক ঋণে সুদ দৈনিক হিসাবে ধরা হয়, হার বাজারের সঙ্গে বদলায়, কিস্তি দেরিতে দিলে জরিমানা যোগ হয়, আর হিসাব পয়সায় গোল করা হয়। ব্লকে দেখেছেন, শুধু গোলাকরণেই বারো মাস পরে কয়েক টাকার পার্থক্য আসে, কারণ recurrence প্রতিটি ছোট ত্রুটিকে পরের মাসে (1 + r) দিয়ে গুণ করে বয়ে নেয়। তাই এই পর্বের সংখ্যাগুলো শুধু গাণিতিক উদাহরণ; কোনো আর্থিক সিদ্ধান্তে প্রতিষ্ঠানের আসল শর্ত আর বিশেষজ্ঞের পরামর্শ লাগে।
তবু মডেলটি মূল্যবান, কারণ সে গঠন ঠিক ধরে। Closed form দেখায় সুদের প্রভাব সূচকীয়: gn পদটি। এ থেকে বোঝা যায় কেন ঋণের মেয়াদ দ্বিগুণ করলে মোট সুদ দ্বিগুণের বেশি বাড়তে পারে, আর কেন শুরুর কিস্তিগুলোর বড় অংশ সুদে যায়। গণিতবিদ George Box-এর নামে প্রচলিত একটি কথা আছে, “সব মডেলই ভুল, কিছু কাজের”, যা এখানে খাটে।
এই পর্বের অন্য মডেলগুলোতেও একই প্রশ্ন। খরগোশ মরে না, এই অনুমান ভুল, তাই জনসংখ্যার বাস্তব হিসাবে Fibonacci চলে না। রেখার সূত্র general position ছাড়া ভাঙে: তিনটি সমান্তরাল রেখায় ৪ ভাগ, এক বিন্দু দিয়ে যাওয়া তিনটিতে ৬, ৭ নয়। মডেল বিশ্বাস করার নিয়ম তাই তিনটি: অনুমানগুলো স্পষ্ট লিখুন; ছোট ক্ষেত্রে সরাসরি গণনা বা বাস্তব তথ্যের সঙ্গে মেলান; আর জানুন কোন অনুমান ভাঙলে কী হয়। গণিতের প্রমাণ শুধু বলে “অনুমান সত্য হলে ফল সত্য”; অনুমান বাস্তবে সত্য কি না, তা আলাদা প্রশ্ন।
মূল উপলব্ধি: Recurrence মডেলের ফল তার অনুমানের মতোই নির্ভরযোগ্য; অনুমান স্পষ্ট লেখা আর ছোট ক্ষেত্রে মেলানো ভালো মডেলের প্রথম শর্ত।
প্র ০৫Algorithm বিশ্লেষণে recurrence কীভাবে এল? Merge sort-এর W(n) = 2W(n/2) + n থেকে কী শেখা যায়, আর n যখন 2-এর ঘাত নয় তখন কী হয়?
Algorithm বিশ্লেষণে recurrence-এর ব্যবহার পুরোনো। ১৮৪৪ সালে Gabriel Lamé Euclid-এর algorithm-এর ধাপ-সংখ্যা Fibonacci সংখ্যা দিয়ে সীমিত করেন, যা প্রায়ই প্রথম দিকের algorithm বিশ্লেষণের একটি উদাহরণ ধরা হয় (পর্ব ১২, ১৩)। বিশ শতকের মাঝামাঝি John von Neumann merge sort-এর ধারণা দেন বলে সাধারণত বলা হয়, আর ১৯৬০-এর দশক থেকে Donald Knuth-এর The Art of Computer Programming বইগুলো recurrence, generating function আর নির্ভুল গণনাকে algorithm বিশ্লেষণের মূল হাতিয়ার বানায়। পরে Bentley, Haken আর Saxe-এর ১৯৮০ সালের কাজে divide-and-conquer recurrence-এর একটি সাধারণ পদ্ধতি আসে, যা আজ master theorem নামে পরিচিত পাঠ্যবইয়ের সূত্রের পূর্বসূরি বলে উল্লেখ করা হয়।
W(n) = 2W(n/2) + n থেকে মূল শিক্ষা: recursion tree-র প্রতিটি স্তরে মোট কাজ সমান, n, আর স্তর log2 n-টি। কাজ যদি স্তরে স্তরে বাড়ত বা কমত, যেমন T(n) = 3T(n/2) + n বা T(n) = T(n/2) + n, উত্তর আলাদা হতো; কোন স্তর প্রাধান্য পায়, তা-ই master theorem-এর তিন ক্ষেত্রের মূল কথা, যা পর্ব ৪৭-এ প্রমাণসহ আসবে।
n যখন 2-এর ঘাত নয়, তখন আসল recurrence W(n) = W(⌊n/2⌋) + W(⌈n/2⌉) + (n − 1)-এর মতো। নির্ভুল সমাধানটি একটু জটিল, কিন্তু যুক্তি সহজ: W একটি ক্রমবর্ধমান ফাংশন হলে, n-কে তার ঠিক ওপরের 2-এর ঘাতে তুলে দিলে কাজ বড়জোর বাড়ে, আর সেই ঘাত n-এর দ্বিগুণের কম। তাই বৃদ্ধির হার একই থাকে। এই monotonicity যুক্তি যে সত্যিই খাটে, তা প্রমাণ করা লাগে, শুধু ধরে নেওয়া যায় না; পর্ব ৪৭ ঠিক এই কাজটি সৎভাবে করবে। DSA কোর্সের পর্ব ২১-এ merge sort-এর কোড আর তার আসল তুলনার সংখ্যা দেখা যায়।
মূল উপলব্ধি: Algorithm-এর চলার সময় নিজেই একটি recurrence মেনে চলে; recurrence সমাধানের গণিত তাই algorithm তুলনার গণিত।
অনুশীলন
আগে খাতায় করুন, তারপর উত্তর খুলে মেলান।
-
প্রমাণ: bn = দৈর্ঘ্য n-এর এমন bit string-এর সংখ্যা, যাতে পরপর তিনটি 0 (“000”) নেই। প্রমাণ করুন n ≥ 4-এ bn = bn−1 + bn−2 + bn−3, initial condition-গুলো বের করুন, আর b8 হিসাব করুন।
- শুরুর মান: দৈর্ঘ্য 1, 2-এ কোনো string-এ “000” থাকতে পারে না, তাই b1 = 2, b2 = 4; দৈর্ঘ্য 3-এ শুধু 000 বাদ, b3 = 7. — তালিকা
- n ≥ 4-এ একটি বৈধ string-এর শেষে 0-এর শেষ দৌড় দেখুন: শেষ অংশটি ঠিক “1”, “10” বা “100” (শেষে তিনটি 0 সম্ভব নয়)। তিনটি দল পরস্পর বিচ্ছিন্ন আর মিলে সব বৈধ string ঢাকে। — ক্ষেত্রভাগ
- শেষে “1”: সামনের n − 1 bit যেকোনো বৈধ string, শেষে 1 যোগ করলে নতুন “000” হয় না: bn−1টি। একইভাবে “10”-এ সামনের n − 2 bit বৈধ: bn−2, আর “100”-এ সামনের n − 3: bn−3. উল্টো দিকে প্রতিটি বৈধ string এভাবেই ভাঙে। — bijection
- যোগের নিয়মে bn = bn−1 + bn−2 + bn−3. ∎ — যোগের নিয়ম
মান: b4 = 7 + 4 + 2 = 13, b5 = 24, b6 = 44, b7 = 81, b8 = 149. Order 3, তাই তিনটি initial condition. (খালি string ধরলে b0 = 1 আর b3 = b2 + b1 + b0 = 7-ও মেলে।) সব মান scratch-এ itertools দিয়ে brute force গুনে মেলানো।
-
হিসাব: (ক) ১০০০ জনের বৃত্তে প্রতি দ্বিতীয় জন বাদ গেলে কে থাকেন? (খ) general position-এ ১২টি রেখায় সমতলের কয়টি অংশ? (গ) 3 × 6 বোর্ডে domino টালি কয়টি, u(n) = 4u(n − 2) − u(n − 4) দিয়ে?
(ক) 1000 = 512 + 488, তাই J(1000) = 2 · 488 + 1 = 977. Binary-তে 1111101000 → 1111010001 = 977.
(খ) L(12) = 12 · 13/2 + 1 = 79.
(গ) u(0) = 1, u(2) = 3, u(4) = 4 · 3 − 1 = 11, u(6) = 4 · 11 − 3 = 41. তিনটি উত্তরই পর্বের Python ব্লকের brute force আউটপুটের সঙ্গে মেলে।
-
আউটপুট অনুমান: কোড না চালিয়ে বলুন কী ছাপা হবে। তারপর যেকোনো Python-এ চালিয়ে মেলান।
a = [2, 1] for n in range(2, 8): a.append(a[-1] + a[-2]) print(a) h = 0 for n in range(1, 7): h = 2 * h + 1 print(h, 2**6 - 1)[2, 1, 3, 4, 7, 11, 18, 29] 63 63প্রথম লাইন: Fibonacci-র একই recurrence, কিন্তু শুরু 2, 1, তাই Lucas সংখ্যা; লুপ n = 2 থেকে 7, ছয়টি নতুন পদ, মোট আটটি। দ্বিতীয় লাইন: h = 0 থেকে ছয়বার h = 2h + 1, অর্থাৎ H(6) = 26 − 1 = 63, closed form-এর সঙ্গে মিলল। আউটপুট CPython-এ চালিয়ে মেলানো।
-
প্রমাণের ভুল ধরুন: একজন লিখলেন: “দাবি: যেকোনো n-টি রেখা সমতলকে ঠিক n(n + 1)/2 + 1 ভাগ করে। প্রমাণ: n-তম রেখা আগের n − 1টি রেখাকে n − 1টি বিন্দুতে কাটে, তাই n-টি টুকরো হয়, প্রতিটি একটি পুরোনো অংশকে দুই ভাগ করে; তাই L(n) = L(n − 1) + n, আর induction-এ দাবি সত্য। ∎” ভুলটি কোথায়, আর একটি counterexample দিন।
- ভুল ধাপ: “আগের n − 1টি রেখাকে n − 1টি বিন্দুতে কাটে”. এটি সত্য শুধু যদি নতুন রেখা কোনোটির সমান্তরাল না হয় (তবে ছেদবিন্দু কম) আর কোনো ছেদবিন্দু দুটি পুরোনো রেখার মিলনবিন্দু না হয় (তবে বিন্দু একই হয়ে যায়)। দাবিতে general position-এর শর্ত বাদ পড়েছে।
- Counterexample: তিনটি সমান্তরাল রেখা সমতলকে ৪ ভাগ করে; এক বিন্দু দিয়ে যাওয়া তিনটি রেখা ৬ ভাগ করে; সূত্র বলত ৭.
- সঠিক বক্তব্য: “general position-এ n-টি রেখা ঠিক n(n + 1)/2 + 1 ভাগ করে, আর যেকোনো n-টি রেখা বড়জোর এতগুলো ভাগ করে।” দ্বিতীয় অংশের জন্য যুক্তি: নতুন রেখার ছেদবিন্দু বড়জোর n − 1টি, তাই নতুন অংশ বড়জোর n-টি।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি গণনার সমস্যার recurrence মডেল বানানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- an কী গোনে, তা নিখুঁতভাবে লেখা
- n = 0, 1, 2, 3-এর মতো ছোট ক্ষেত্র হাতে গোনা
- শেষ বা প্রথম ধাপ ধরে বস্তুগুলোকে বিচ্ছিন্ন দলে ভাগ করা
- Recurrence আর initial condition লেখা, brute force-এ মেলানো
- Unrolling করে closed form অনুমান আর induction-এ প্রমাণ
H(1) = 1 আর n ≥ 2-এ H(n) = 2H(n − 1) + 1 হলে H(6) কত?
- 32
- 63
- 64
- 127
দৈর্ঘ্য 6-এর কয়টি bit string-এ পরপর দুটি 0 (“00”) নেই?
- 13
- 21
- 34
- 32
2 × 8 আকারের একটি বোর্ডকে 1 × 2 domino দিয়ে পুরো ঢাকার কয়টি উপায় আছে?
- 21
- 34
- 55
- 64
২১ জন বৃত্তে বসে, 1 নম্বর থেকে শুরু করে প্রতি দ্বিতীয় জন বাদ যান (1 থাকেন, 2 বাদ, 3 থাকেন, …)। শেষে কে থাকেন?
- 11
- 21
- 9
- 1
৮টি সরলরেখা, যার কোনো দুটি সমান্তরাল নয় আর কোনো তিনটি এক বিন্দু দিয়ে যায় না, সমতলকে কয়টি অংশে ভাগ করে?
- 37
- 29
- 256
- 36
an = 3an−2 (n ≥ 2) recurrence-টির একটি সমাধান অনন্যভাবে ঠিক করতে কয়টি initial condition লাগে?
- ০টি
- ১টি
- ২টি
- ৩টি
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- সংজ্ঞা আর অনন্যতা: order k-এর recurrence আর ঠিক k-টি initial condition মিলে একটিই ধারা দেয় (strong induction); কম দিলে অসীম সমাধান, ভুল দিলে পুরো ধারা ভুল।
- মডেলের কৌশল: an কী গোনে লিখুন, ছোট ক্ষেত্র গুনুন, শেষ বা প্রথম ধাপ ধরে বিচ্ছিন্ন দলে ভাগ করুন, যোগ আর গুণের নিয়মে recurrence লিখুন।
- Fibonacci পরিবার: খরগোশ Fn, ছন্দ আর 2 × n টালি Fn+1, “00”-হীন bit string Fn+2; “000” নিষেধে order 3.
- Unrolling আর প্রমাণ: Hanoi 2n − 1 (আর এটিই সর্বনিম্ন, আলাদা lower bound প্রমাণ), রেখা n(n + 1)/2 + 1 (general position-এ), ঋণ gnB0 − P(gn − 1)/r.
- Catalan আর Josephus: Cn+1 = ∑ CiCn−i (root ধরে ভাগ); J(2m + l) = 2l + 1, binary-তে প্রথম bit শেষে সরানো।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪৫ · Linear recurrence সমাধান — characteristic equationপরবর্তী পর্বCharacteristic equation দিয়ে order 2-এর recurrence-এর সাধারণ সমাধান।
- পর্ব ১২ · Recursive সংজ্ঞা আর structural inductionপুনরালোচনাRecursive সংজ্ঞা, Fibonacci-র বৃদ্ধি আর Cassini-র সূত্র।
- পর্ব ৩৭ · গণনার কৌশল — lattice path, bijection আর double countingপুনরালোচনাCatalan সংখ্যার closed form আর “00”-হীন bit string-এর প্রমাণ।
- ডিসক্রিট ম্যাথমেটিক্স · কোর্স হোম৮৩টি পর্বপুরো কোর্সের মানচিত্র আর ১৪টি মডিউল।
- DSA · পর্ব ০৮ · Recurrence সমাধান — substitution আর recursion treeকোডে প্রয়োগAlgorithm বিশ্লেষণে recurrence-এর ব্যবহার।
- DSA · পর্ব ০৭ · রিকার্শন — নিজেকে ডাকা ফাংশন আর call stackকোডে প্রয়োগHanoi-র recursive কোড আর call stack.