কনটেক্সট-ফ্রি ল্যাঙ্গুয়েজের জন্য পাম্পিং লেমা
এই পাঠে যা শিখবেন
- CFL পাম্পিং লেমার সঠিক, ফরমাল বিবৃতি — পাঁচ-অংশ বিভাজন ও তিনটি শর্ত
- এর পেছনের pigeonhole যুক্তি — পার্স ট্রি-এর উচ্চতা ও ভ্যারিয়েবল-পুনরাবৃত্তি
- $a^nb^nc^n$ কনটেক্সট-ফ্রি নয় তার সম্পূর্ণ contradiction-ভিত্তিক প্রমাণ
- Python-এ একটি exhaustive split-checker যা প্রতিটি সম্ভাব্য বিভাজনের বিরুদ্ধে প্রমাণ verify করে
১ · CFL পাম্পিং লেমার বিবৃতি
M3/L13 রেগুলার ল্যাঙ্গুয়েজের জন্য একটি পাম্পিং লেমা দিয়েছিল — এখন M4-M5-এ CFL নিয়ে কাজ করার পর, একই ধরনের কিন্তু আরও জটিল একটি প্রপার্টি CFL-এর জন্যও প্রমাণ করা যায়:
যদি $L$ কনটেক্সট-ফ্রি হয়, তাহলে একটি পাম্পিং লেংথPumping lengthএকটি নির্দিষ্ট সংখ্যা $p$, ভাষা $L$-এর উপর নির্ভরশীল, যার চেয়ে বড় দৈর্ঘ্যের প্রতিটি স্ট্রিং পাম্পিং প্রপার্টি মেনে চলতে বাধ্য $p \geq 1$ আছে যেন প্রতিটি স্ট্রিং $s \in L$ যেখানে $|s| \geq p$, সেটিকে পাঁচ ভাগে ভাগ করা যায় $s = uvxyz$, যা নিচের শর্তগুলো মেনে চলে:
- (১) $|vy| > 0$ (দুটো পাম্পড অংশের অন্তত একটি খালি নয়)
- (২) $|vxy| \leq p$ (মাঝের তিন অংশ একসাথে $p$-এর মধ্যে সীমাবদ্ধ)
- (৩) সব $i \geq 0$-এর জন্য, $uv^ixy^iz \in L$ ($v$ আর $y$ — দুটোকেই একইসাথে, একই সংখ্যকবার — "পাম্প" করলেও ফলাফল $L$-এই থেকে যায়)
$$\forall s \in L, |s| \geq p \implies \exists\, u,v,x,y,z: s=uvxyz \land |vy|>0 \land |vxy|\leq p \land \forall i\geq 0, uv^ixy^iz \in L$$
২ · কেন এটি সত্য — পার্স ট্রি-এর উচ্চতা ও Pigeonhole
L13-এ যুক্তিটি ছিল DFA-এর ফিক্সড সংখ্যক স্টেট নিয়ে — লম্বা স্ট্রিং মানেই কোনো স্টেট পুনরায় ভিজিট হবে (pigeonhole)। CFL-এর জন্য যুক্তিটি একইরকম, কিন্তু এবার পার্স ট্রি-এর উচ্চতা নিয়ে কাজ করে:
যথেষ্ট লম্বা স্ট্রিং-এর জন্য, তার CFG পার্স ট্রি (CNF-আকারে, L20 — যা নিশ্চিত করে প্রতিটি ইন্টারনাল নোডের ঠিক দুটো চাইল্ড আছে) root থেকে leaf পর্যন্ত এমন একটি পাথ ধারণ করবে যার দৈর্ঘ্য গ্রামারের মোট ভ্যারিয়েবল সংখ্যার চেয়ে বেশি — pigeonhole অনুযায়ী, সেই পাথে কোনো ভ্যারিয়েবল অবশ্যই পুনরাবৃত্তি হবে। সেই পুনরাবৃত্ত ভ্যারিয়েবলের দুটো occurrence-এর মাঝের subtree-টি "পাম্পযোগ্য" — একটি occurrence-এর subtree অন্যটির জায়গায় বসিয়ে দিলে (duplicate বা remove করে) একটি নতুন বৈধ পার্স ট্রি পাওয়া যায়। এই subtree-এর দ্বারা উৎপন্ন অংশই $v$ (বামে) আর $y$ (ডানে), আর তার ভেতরের অংশ $x$।
৩ · প্রমাণ — $a^nb^nc^n$ কনটেক্সট-ফ্রি নয়
L18-এর গ্রামার-ভিত্তিক উদাহরণে $a^nb^nc^n$ দেখা গিয়েছিল "inherently ambiguous" প্রসঙ্গে। এখন CFL পাম্পিং লেমা দিয়ে দেখানো যাক এই ভাষাটি আসলে কনটেক্সট-ফ্রিই নয়:
- (১) ধরে নিন $L = \{a^nb^nc^n : n \geq 0\}$ কনটেক্সট-ফ্রি — তাহলে একটি পাম্পিং লেংথ $p$ থাকতেই হবে।
- (২) নির্বাচন করুন $s = a^pb^pc^p \in L$, যার দৈর্ঘ্য $3p \geq p$।
- (৩) যেকোনো বৈধ বিভাজন $s = uvxyz$ শর্ত $|vxy| \leq p$ মেনে — এর মানে $vxy$ ব্লকটি $a$, $b$, $c$ — তিনটি symbol-block-এর সবগুলোতে একসাথে বিস্তৃত হতে পারবে না (কারণ প্রতিটি ব্লকের দৈর্ঘ্য $p$, আর তিনটি ব্লক মিলিয়ে দৈর্ঘ্য $3p$, কিন্তু $vxy \leq p$ হওয়ায় এটি সর্বোচ্চ দুটি সংলগ্ন ব্লক স্পর্শ করতে পারে)।
- (৪) পাম্পিং (যেমন $i=2$, দ্বিগুণ করা) তাই হয় কিছু symbol-এর সংখ্যা বাড়িয়ে দেবে অন্যগুলো অপরিবর্তিত রেখে — $a,b,c$-এর সংখ্যার সমতা ভেঙে যাবে — contradiction, যেহেতু $L$ ধরে নেওয়া হয়েছিল $n=n=n$ ভাষা।
- (৫) সিদ্ধান্ত: $L = \{a^nb^nc^n\}$ কনটেক্সট-ফ্রি নয়।
L22-এ বলা হয়েছিল একটি PDA-এর স্ট্যাক একটিমাত্র সংখ্যা "গুনতে" পারে (যেমন $0^n1^n$-এ 0-এর সংখ্যা)। কিন্তু $a^nb^nc^n$-এ একসাথে দুটো স্বতন্ত্র সমতা ($a$-এর সংখ্যা = $b$-এর সংখ্যা = $c$-এর সংখ্যা) যাচাই করতে হয় — একটি স্ট্যাক দিয়ে এই তিন-মুখী সমতা রক্ষা করা সম্ভব না, ঠিক এই কারণেই এটি CFL-এর সীমার বাইরে চলে যায়। এই ভাষার জন্য M7/L29-এ একটি context-sensitive গ্রামার লাগবে।
৪ · Python-এ Exhaustive Split-Checking
নিচের কোড সেলে একটি সত্যিকারের check_cfl_pumping_violation ফাংশন — একটি নির্দিষ্ট $s$ ও claimed $p$-এর জন্য প্রতিটি সম্ভাব্য বৈধ পাঁচ-ভাগ বিভাজন ($|vxy|\leq p$, $|vy|>0$ শর্ত মেনে) enumerate করে, এবং প্রতিটির জন্য $i=2$ দিয়ে পাম্প করে $a^nb^nc^n$-এ membership হারায় কিনা যাচাই করে — এটি হাতে-বাছাই করা একটি বিভাজন নয়, বরং সব সম্ভাব্য বিভাজনের উপর একটি সম্পূর্ণ, exhaustive যাচাই।
# L26 -- CFL পাম্পিং লেমা, a^n b^n c^n কনটেক্সট-ফ্রি নয় তার exhaustive প্রমাণ
def is_anbncn(s):
"""L = {a^n b^n c^n : n >= 0}-এর জন্য মেম্বারশিপ চেকার -- regex ছাড়া, সরাসরি স্ক্যান"""
i, n = 0, len(s)
a_count = 0
while i < n and s[i] == "a":
a_count += 1; i += 1
b_count = 0
while i < n and s[i] == "b":
b_count += 1; i += 1
c_count = 0
while i < n and s[i] == "c":
c_count += 1; i += 1
if i != n: # ক্রম ভুল থাকলে (a*b*c* না হলে)
return False
return a_count == b_count == c_count
def all_five_way_splits(s, p):
"""s = uvxyz -- |vxy| <= p এবং |vy| > 0 শর্ত মানা সব সম্ভাব্য বিভাজন yield করে"""
n = len(s)
for vxy_start in range(0, n + 1):
max_vxy_len = min(p, n - vxy_start)
for vxy_len in range(0, max_vxy_len + 1):
vxy_end = vxy_start + vxy_len
u, vxy, z = s[:vxy_start], s[vxy_start:vxy_end], s[vxy_end:]
for v_len in range(0, vxy_len + 1):
for y_len in range(0, vxy_len - v_len + 1):
x_len = vxy_len - v_len - y_len
if v_len + y_len == 0:
continue # শর্ত (1): |vy| > 0
v = vxy[:v_len]
x = vxy[v_len:v_len + x_len]
y = vxy[v_len + x_len:]
yield (u, v, x, y, z)
def check_cfl_pumping_violation(s, p, membership_fn, i=2):
"""s-এর প্রতিটি বৈধ পাঁচ-ভাগ বিভাজনে i=2 দিয়ে পাম্প করে membership ভাঙে কিনা চেক করে।
রিটার্ন করে (সবগুলো বিভাজনই violate করেছে কিনা, মোট কতগুলো বিভাজন, প্রথম ব্যতিক্রম আছে কি)।"""
total = 0
non_violating = None
for (u, v, x, y, z) in all_five_way_splits(s, p):
total += 1
pumped = u + v * i + x + y * i + z
if membership_fn(pumped) and non_violating is None:
non_violating = (u, v, x, y, z, pumped) # প্রমাণ ভেঙে যাবে যদি এটি ঘটে
return (non_violating is None, total, non_violating)
for p in (2, 3, 4):
s = "a" * p + "b" * p + "c" * p
all_violate, total, counterexample = check_cfl_pumping_violation(s, p, is_anbncn, i=2)
print(f"p={p} s={s!r} মোট-বিভাজন-চেক-করা={total} প্রতিটিই-violate-করেছে={all_violate}")
if counterexample:
print(" ব্যতিক্রম পাওয়া গেছে (প্রমাণ ভেঙে যেত):", counterexample)
print()
print("সিদ্ধান্ত: প্রতিটি পরীক্ষিত p-এর জন্য, a^p b^p c^p-এর প্রতিটি সম্ভাব্য পাঁচ-ভাগ বিভাজন,")
print("i=2 দিয়ে পাম্প করলে a^n b^n c^n ভাষার বাইরে চলে যায় -- অর্থাৎ কোনো পাম্পিং লেংথ")
print("থাকতেই পারে না (contradiction), তাই L = {a^n b^n c^n} কনটেক্সট-ফ্রি নয় (QED)।")
all_five_way_splits-এ vxy_start স্ট্রিং-এর যেকোনো পজিশন থেকে শুরু হতে পারে (শুধু শুরু থেকে নয়) — এটাই M3/L13-এর রেগুলার-পাম্পিং লেমার শর্ত (2)-এর ($|xy|\leq p$, যা সবসময় স্ট্রিং-এর একেবারে শুরু থেকে গণনা করা হতো) থেকে CFL-ভার্সনের একটি গুরুত্বপূর্ণ পার্থক্য — এখানে $vxy$ ব্লকটি স্ট্রিং-এর যেকোনো জায়গায় থাকতে পারে, শুধু তার নিজের দৈর্ঘ্য $p$-এর মধ্যে সীমাবদ্ধ থাকতে হয়।
CFL পাম্পিং লেমা M3/L13-এর রেগুলার-ল্যাঙ্গুয়েজ পাম্পিং লেমার একই যুক্তির (contradiction, pigeonhole) একটি গভীরতর সংস্করণ — এখন পার্স-ট্রি-উচ্চতার উপর ভিত্তি করে, পাঁচ-ভাগ বিভাজন আর দুটো সিমাল্টেনিয়াস পাম্পড অংশ নিয়ে। $a^nb^nc^n$ এর ক্লাসিক উদাহরণ দেখায় — একটি একক স্ট্যাক দিয়ে দুটো স্বতন্ত্র সমতা একসাথে রক্ষা করা যায় না, যা CFL-এর একটি মৌলিক সীমা নির্দেশ করে (L27-এ CFL ক্লোজার প্রপার্টির আলোচনায় এই একই উদাহরণ আবার কাজে লাগবে)।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ CFL পাম্পিং লেমায় কেন দুটো অংশ ($v$ ও $y$) পাম্প করতে হয়, শুধু একটি ($y$, রেগুলার লেমার মতো) নয়?
কারণ CFG-এর পার্স ট্রি-তে একটি ভ্যারিয়েবল পুনরাবৃত্তি হলে, তার subtree-টি স্ট্রিং-এর একটি একক সংলগ্ন অংশ উৎপন্ন করে না — বরং সেই subtree-এর ভেতরের ভ্যারিয়েবল (যা root-এ আবার একই সিম্বল) নিজেও আরও কিছু উৎপন্ন করে, ফলে subtree-টি তার বাম দিকে কিছু টার্মিনাল ($v$) আর ডান দিকে কিছু টার্মিনাল ($y$) উৎপন্ন করে, মাঝখানে $x$ রেখে। DFA-ভিত্তিক রেগুলার প্রমাণে এমন কোনো "ভেতরের গঠন" ছিল না — শুধু একটি লুপ, তাই একটি অংশ ($y$) যথেষ্ট ছিল।
প্র ০২ $a^nb^nc^n$-এর প্রমাণে কেন $s = a^pb^pc^p$ বেছে নেওয়া হলো, অন্য কোনো স্ট্রিং নয়?
কারণ এই নির্দিষ্ট গঠনটাই নিশ্চিত করে যে $vxy$ ব্লক (দৈর্ঘ্য $\leq p$) কখনোই তিনটি symbol-block একসাথে স্পর্শ করতে পারবে না — এটাই প্রমাণের কেন্দ্রীয় কৌশল। যদি স্ট্রিংটি এমনভাবে বাছাই করা হতো যেখানে $vxy$ সহজেই সব ব্লক স্পর্শ করতে পারতো (যেমন খুব ছোট একটি স্ট্রিং), তাহলে হয়তো এমন একটি বিভাজন পাওয়া যেত যা পাম্প করলেও ভাষায়ই থেকে যেত — প্রমাণটি তখন কাজ করতো না। L14-এর মতোই, স্ট্রিং বাছাই এই ধরনের প্রমাণের "শিল্প" (art)।
প্র ০৩ যদি $vxy$ ব্লক শুধু $a$ আর $b$ (দুটো ব্লক, তিনটি নয়) স্পর্শ করে, তাহলে দ্বিগুণ করলে ($i=2$) কেন এখনও ভাষার বাইরে চলে যায়?
যদি $vxy$ শুধু $a$ আর $b$-এর সীমানা স্পর্শ করে (কিন্তু $c$ একেবারেই স্পর্শ করে না), তাহলে দ্বিগুণ করলে $a$-এর সংখ্যা এবং/অথবা $b$-এর সংখ্যা বাড়বে, কিন্তু $c$-এর সংখ্যা ঠিক $p$-ই থেকে যাবে (একদম অপরিবর্তিত)। যেহেতু ভাষায় থাকতে হলে তিনটি সংখ্যাই সমান হতে হয়, আর এখন অন্তত একটি (a অথবা b) বেড়ে গেছে অথচ c অপরিবর্তিত, তাই $a$-সংখ্যা = $b$-সংখ্যা = $c$-সংখ্যা শর্তটি ভেঙে যায় — membership হারায়। এই যুক্তিটি $vxy$-এর সব সম্ভাব্য অবস্থানের জন্যই একইভাবে কাজ করে, যা কোডের exhaustive চেক নিশ্চিত করে দেখিয়েছে।
অনুশীলন
-
চিন্তা করুন: $L = \{a^nb^n : n \geq 0\}$ (শুধু দুটো ব্লক, তিনটি নয়) কি CFL পাম্পিং লেমা দিয়ে "কনটেক্সট-ফ্রি নয়" প্রমাণ করা যাবে? কেন বা কেন নয়?
না — $\{a^nb^n\}$ আসলে কনটেক্সট-ফ্রিই (একটি সহজ CFG $S \to aSb \mid \varepsilon$ দিয়ে জেনারেট করা যায়, এবং L22-এর মতো একটি সাধারণ PDA দিয়েও স্বীকৃত হয়)। তাই CFL পাম্পিং লেমা এই ভাষার জন্য প্রযোজ্য এবং সন্তুষ্ট — এটি প্রমাণ করার চেষ্টা করলে দেখা যাবে যেকোনো বিভাজনে পাম্পিং সবসময় ভাষাতেই থেকে যায় (অন্তত একটি বৈধ বিভাজন পাওয়া যাবে যা কাজ করে) — মনে রাখবেন, পাম্পিং লেমা শুধু একটি necessary শর্ত, তাই এটি "প্রমাণ করতে ব্যর্থ হওয়া" মানেই ভাষা নন-কনটেক্সট-ফ্রি নয়।
-
পরীক্ষা করুন: কোড সেলে
p=5যোগ করে চালান —মোট-বিভাজন-চেক-করাসংখ্যাটি $p$ বাড়ার সাথে সাথে কীভাবে বাড়ে তা লক্ষ্য করুন।$p$ বাড়ার সাথে সাথে সম্ভাব্য বিভাজনের সংখ্যা দ্রুত বাড়ে (স্ট্রিং দৈর্ঘ্য $3p$, আর $vxy$-এর সম্ভাব্য অবস্থান ও দৈর্ঘ্য, তার ভেতরে $v$/$x$/$y$-এর সম্ভাব্য বিভাজন — সব মিলিয়ে polynomial বৃদ্ধি) — তবুও, $p=5$-এ
প্রতিটিই-violate-করেছে=Trueথাকা উচিত, প্রমাণের সাধারণতা (generality) আবার নিশ্চিত করে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ L27-এ CFL ক্লোজার প্রপার্টি দেখুন — এই পাঠের $a^nb^nc^n$ উদাহরণ সেখানে "CFL ইন্টারসেকশনে ক্লোজড নয়" প্রমাণের একটি কনক্রিট কাউন্টার-এক্সাম্পল হিসেবে আবার ব্যবহৃত হবে।
- L13 · রেগুলার ল্যাঙ্গুয়েজের জন্য পাম্পিং লেমা M3 এই পাঠের CFL পাম্পিং লেমার মূল, সরল সংস্করণ — একটি অংশ ($y$) পাম্প করা এবং DFA-স্টেট-কাউন্ট-ভিত্তিক pigeonhole যুক্তি।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git ও Theory of Computation — সব এক জায়গায়।