রেগুলার ল্যাঙ্গুয়েজের জন্য পাম্পিং লেমা
এই পাঠে যা শিখবেন
- পাম্পিং লেমার নিখুঁত ফরমাল স্টেটমেন্ট — তিনটি শর্তসহ
- কেন এটি সত্য — পিজনহোল প্রিন্সিপলের সাথে DFA-এর স্টেট-পুনরাবৃত্তির সংযোগ
- এই লেমা কী প্রমাণ করে না — "necessary but not sufficient" ফাঁদ বোঝা
- Python-এ real
find_pumping_split— DFA সিমুলেট করে পিজনহোল আর্গুমেন্ট কোড হিসেবে বাস্তবায়ন - $xy^0z$, $xy^1z$, $xy^2z$, $xy^3z$ — চারটিই DFA-তে সরাসরি টেস্ট করে পাম্পিং প্রপার্টি নিশ্চিতভাবে যাচাই
১ · পাম্পিং লেমা কী, এবং কী নয়
পাম্পিং লেমাPumping Lemmaপ্রতিটি রেগুলার ল্যাঙ্গুয়েজের একটি নিশ্চিত কাঠামোগত ধর্ম, যা DFA-র ফাইনাইট স্টেট-সংখ্যা থেকে সরাসরি অনুসৃত হয়। হলো এমন একটি ধর্ম যা প্রতিটি রেগুলার ল্যাঙ্গুয়েজ মেনে চলতেই হয় — কিন্তু এই ধর্ম মেনে চলা কোনো ল্যাঙ্গুয়েজকে রেগুলার হওয়ার গ্যারান্টি দেয় না (necessary but not sufficient)। এর বাস্তব ব্যবহারিক মূল্য উল্টো দিক থেকে — L14-এ দেখা যাবে, যদি কোনো ল্যাঙ্গুয়েজ এই ধর্ম মেনে না চলে, তাহলে কনট্রাপজিটিভ (contrapositive) যুক্তিতে সেই ল্যাঙ্গুয়েজ নিশ্চিতভাবে রেগুলার নয় — এটিই এই লেমার সবচেয়ে গুরুত্বপূর্ণ ব্যবহার।
২ · ফরমাল স্টেটমেন্ট
যদি $L$ একটি রেগুলার ল্যাঙ্গুয়েজ হয়, তাহলে একটি পূর্ণসংখ্যা $p \geq 1$ (পাম্পিং লেংথ) থাকে যাতে — দৈর্ঘ্য $|s| \geq p$ এমন প্রতিটি স্ট্রিং $s \in L$-কে $s = xyz$ আকারে ভাগ করা যায়, যেখানে:
- (১) $|y| > 0$ — "পাম্প" করা অংশটি খালি নয়
- (২) $|xy| \leq p$ — বিভাজনটি স্ট্রিং-এর প্রথম $p$ ক্যারেক্টারের মধ্যেই ঘটে
- (৩) সব $i \geq 0$-এর জন্য, $xy^iz \in L$ — মাঝের অংশটি যতবার ইচ্ছা পুনরাবৃত্তি (i=0 সহ, অর্থাৎ পুরোপুরি বাদ দিয়েও) করলেও ফলাফল $L$-এর মধ্যেই থাকে
৩ · কেন এটি সত্য — পিজনহোল প্রিন্সিপল
ধরুন $L$ রেগুলার, তাই L06-এর সংজ্ঞা অনুযায়ী একটি DFA $M$ আছে যার ঠিক $p$টি স্টেট এবং $L(M) = L$। এখন দৈর্ঘ্য $\geq p$ এমন কোনো স্ট্রিং $s$ প্রসেস করার সময় DFA প্রথম $p$টি ট্রানজিশনে $p+1$টি স্টেট ভিজিট করে (শুরুর স্টেটসহ) — কিন্তু DFA-তে মোট স্টেটই আছে মাত্র $p$টি। পিজনহোল প্রিন্সিপলPigeonhole Principle$n+1$টি বস্তু $n$টি বাক্সে রাখলে অন্তত একটি বাক্সে কমপক্ষে দুটো বস্তু পড়তেই হবে — Discrete Mathematics কোর্সে ফরমালি প্রমাণিত একটি মৌলিক কম্বিনেটোরিক্স ফ্যাক্ট। অনুযায়ী — $p+1$টি ভিজিট, $p$টি সম্ভাব্য স্টেট — কোনো একটি স্টেট অন্তত দুইবার ভিজিট হতেই হবে।
সেই পুনরাবৃত্ত স্টেটের দুইবার ভিজিটের মাঝে যে সাবস্ট্রিং প্রসেস হয়েছে, সেটিই $y$। যেহেতু $y$ প্রসেস করার আগে ও পরে DFA একই স্টেটে থাকে, $y$-কে যতবার পুনরাবৃত্তি (বা সম্পূর্ণ বাদ) করা হোক না কেন, বাকি স্ট্রিং $z$ ঠিক একইভাবে প্রসেস হবে — চূড়ান্ত স্টেট অপরিবর্তিত থাকবে, ফলে accept/reject-এর সিদ্ধান্তও অপরিবর্তিত থাকবে। এই স্টেট-পুনরাবৃত্তিই পাম্পিং লেমার তিনটি শর্তের সরাসরি উৎস।
৪ · কোডে পিজনহোল আর্গুমেন্ট — real DFA সিমুলেশন
নিচের কোড সেলে find_pumping_split(dfa, string) ফাংশনটি সত্যিকারের DFA সিমুলেশন চালিয়ে
প্রথম $p$টি ট্রানজিশনের মধ্যে ভিজিট করা স্টেট-সিকোয়েন্স ট্র্যাক করে, এবং প্রথম পুনরাবৃত্ত স্টেট খুঁজে বের
করে ($x$, $y$, $z$ split)। এটি L06-এর "even number of 1s" DFA-তে চালানো হয়েছে, এবং তারপর
$xy^0z, xy^1z, xy^2z, xy^3z$ — চারটি স্ট্রিংই সরাসরি DFA-তে টেস্ট করে দেখানো হয়েছে সবগুলোর accept/reject
ফলাফল মূল স্ট্রিং-এর সাথে অভিন্ন — পাম্পিং প্রপার্টির একটি বাস্তব, রান-টাইম-এ যাচাইকৃত নিশ্চিতকরণ।
class DFA:
def __init__(self, states, alphabet, transitions, start, accept_states):
self.states = list(states)
self.alphabet = set(alphabet)
self.transitions = transitions
self.start = start
self.accept_states = set(accept_states)
for q in self.states:
for a in self.alphabet:
assert (q, a) in self.transitions
def run(self, s):
state = self.start
for ch in s:
state = self.transitions[(state, ch)]
return state
def accepts(self, s):
return self.run(s) in self.accept_states
# L06-এর "even number of 1s" DFA পুনর্ব্যবহার
even_ones = DFA(
states=["q0", "q1"], alphabet={"0", "1"},
transitions={("q0","0"):"q0", ("q0","1"):"q1", ("q1","0"):"q1", ("q1","1"):"q0"},
start="q0", accept_states={"q0"},
)
def find_pumping_split(dfa, string):
p = len(dfa.states)
assert len(string) >= p, "পাম্পিং লেমার জন্য |s| >= p দরকার"
# state_seq[i] = string[:i] প্রসেস করার পর DFA-র স্টেট
state_seq = [dfa.start]
state = dfa.start
for ch in string[:p]: # প্রথম p ট্রানজিশন -- p+1টি ভিজিট -- পিজনহোল নিশ্চিত করে একটি পুনরাবৃত্তি
state = dfa.transitions[(state, ch)]
state_seq.append(state)
seen = {}
first_repeat = None
for i, st in enumerate(state_seq):
if st in seen:
first_repeat = (seen[st], i) # (প্রথমবার ভিজিট, দ্বিতীয়বার ভিজিট)
break
seen[st] = i
assert first_repeat is not None, "পিজনহোল প্রিন্সিপল অনুযায়ী একটি পুনরাবৃত্তি থাকতেই হবে"
j, k = first_repeat
x, y, z = string[:j], string[j:k], string[k:]
assert len(y) > 0 and len(x) + len(y) <= p and x + y + z == string
return x, y, z, state_seq
def verify_pumping(dfa, x, y, z, original_string):
original_result = dfa.accepts(original_string)
results = {}
for i in range(4):
pumped = x + y * i + z
results[i] = (pumped, dfa.accepts(pumped))
all_consistent = all(res == original_result for (_, res) in results.values())
return results, all_consistent
test_string = "1101011" # |s| = 7 >= p = 2
p = len(even_ones.states)
x, y, z, seq = find_pumping_split(even_ones, test_string)
print(f"DFA states = {even_ones.states}, পাম্পিং লেংথ p = {p}")
print(f"s = {test_string!r} (|s| = {len(test_string)})")
print(f"প্রথম p ট্রানজিশনে ভিজিট করা স্টেট-সিকোয়েন্স: {seq}")
print(f"পিজনহোল-এ পাওয়া split: x={x!r}, y={y!r}, z={z!r}")
print(f"শর্ত (১) |y|>0: {len(y)>0} শর্ত (২) |xy|<=p: {len(x)+len(y)} <= {p} -> {len(x)+len(y)<=p}")
print(f"মূল স্ট্রিং-এর ফলাফল: accepted = {even_ones.accepts(test_string)}")
print()
results, consistent = verify_pumping(even_ones, x, y, z, test_string)
for i, (pumped, acc) in results.items():
print(f" i={i}: xy^{i}z = {pumped!r:15s} -> accepted = {acc}")
print()
print("শর্ত (৩) সব xy^iz-ই মূল ফলাফলের সাথে সঙ্গতিপূর্ণ (পাম্পিং প্রপার্টি সত্য):", consistent)
find_pumping_split কোনো ভাষা-নির্দিষ্ট জ্ঞান ব্যবহার করছে না — এটি শুধু DFA-র
স্টেট-সিকোয়েন্স ট্র্যাক করছে এবং পিজনহোল প্রিন্সিপল প্রয়োগ করছে। তাই এই একই ফাংশন যেকোনো
DFA আর দৈর্ঘ্য $\geq p$ যেকোনো স্ট্রিং-এর জন্য কাজ করবে — ঠিক যেমন পাম্পিং লেমার প্রমাণ কোনো নির্দিষ্ট
ল্যাঙ্গুয়েজের উপর নির্ভর করে না, শুধু "DFA-র ফাইনাইট স্টেট আছে" এই ফ্যাক্টের উপর নির্ভর করে।
পাম্পিং লেমা DFA-র ফাইনাইট স্টেট-সংখ্যা থেকে পিজনহোল প্রিন্সিপল দিয়ে সরাসরি অনুসৃত একটি প্রয়োজনীয় (কিন্তু পর্যাপ্ত নয়) ধর্ম। এর আসল শক্তি কনট্রাপজিটিভ দিকে — L14-এ দেখা যাবে, এই ধর্ম ভাঙলে ল্যাঙ্গুয়েজ নিশ্চিতভাবে নন-রেগুলার, এবং এটিই এই থিওরির সবচেয়ে ব্যবহৃত প্রমাণ-কৌশলগুলোর একটি।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ পাম্পিং লেংথ $p$ ঠিক কী প্রতিনিধিত্ব করে — এটি কি সবসময় নির্দিষ্ট, একক কোনো সংখ্যা?
$p$ যেকোনো DFA-র স্টেট-সংখ্যা (বা তার চেয়ে বড়) হতে পারে যা $L$ recognize করে — এটি অনন্য (unique) নয়, শুধু অস্তিত্বশীল (∃)। সাধারণত সবচেয়ে ছোট বৈধ পাম্পিং লেংথ হলো $L$-এর মিনিমাল DFA-র (L16) স্টেট-সংখ্যা — কিন্তু লেমার স্টেটমেন্ট নিজেই বলে না যে $p$ সবচেয়ে ছোট হতে হবে, শুধু বলে কোনো একটি $p$ থাকে যাতে শর্তগুলো সব $|s|\geq p$ স্ট্রিং-এর জন্য সত্য হয়। বড় কোনো $p'$ ($p' > p$) নিলেও শর্তগুলো এখনও সত্যই থাকবে, কারণ $|s| \geq p'$ মানে স্বয়ংক্রিয়ভাবে $|s|\geq p$-ও সত্য।
প্র ০২ শর্ত (২) $|xy| \leq p$ কেন লেমাতে আলাদা করে বলা দরকার — এটি কি স্বয়ংক্রিয়ভাবেই সত্য হয় না?
না, এটি প্রমাণের নির্মাণ-পদ্ধতি থেকেই আসে এবং আলাদাভাবে গুরুত্বপূর্ণ — কারণ এটিই নিশ্চিত করে যে পুনরাবৃত্ত স্টেটটি স্ট্রিং-এর প্রথম অংশেই পাওয়া গেছে (পুরো স্ট্রিং জুড়ে খোঁজার দরকার নেই, শুধু প্রথম $p$ ক্যারেক্টারের মধ্যেই পিজনহোল দিয়ে গ্যারান্টিড)। এই শর্তটি ছাড়া $y$ স্ট্রিং-এর যেকোনো জায়গায় হতে পারত, এবং L14-এর মতো প্রমাণে এই "$y$ শুধু প্রথম $p$ ক্যারেক্টারের মধ্যে থাকবে" শর্তটিই প্রায়ই সবচেয়ে গুরুত্বপূর্ণ যুক্তির ভিত্তি হয় (উদাহরণ: $0^p1^p$-এ প্রথম $p$ ক্যারেক্টার সব 0, তাই $y$-ও নিশ্চিতভাবে শুধু 0 দিয়ে গঠিত)।
প্র ০৩ উপরের কোডে $i=0$ (অর্থাৎ $y$ সম্পূর্ণ বাদ দেওয়া) কেন বিশেষভাবে গুরুত্বপূর্ণ?
কারণ $i=0$ ("পাম্প ডাউন") হলো সেই কেসটি যেখানে স্ট্রিং-এর একটি অংশ সম্পূর্ণ সরিয়ে ফেলা হয় — এটি প্রায়ই সবচেয়ে সহজে একটি "স্ট্রাকচারাল কনস্ট্রেইন্ট" ভাঙে (যেমন $0^n1^n$-এ 0-এর সংখ্যা কমিয়ে দিলে সমতা ভেঙে যায়, L14 দেখুন)। $i=2,3$ ("পাম্প আপ") সাধারণত ভিন্ন ধরনের সমস্যা তৈরি করে। যেহেতু লেমার শর্ত (৩) বলে সব $i\geq 0$-এর জন্য সত্য হতে হবে, একটি মাত্র $i$-এ ব্যর্থ হলেই পুরো শর্তটি ভেঙে যায় — তাই নন-রেগুলারিটি প্রমাণে প্রায়ই শুধু $i=0$ পরীক্ষা করলেই যথেষ্ট হয়, যদিও কোডে আমরা $i=0,1,2,3$ — চারটিই টেস্ট করে অতিরিক্ত নিশ্চয়তা নিয়েছি।
অনুশীলন
-
চিন্তা করুন: যদি একটি DFA-র ৫টি স্টেট থাকে এবং একটি স্ট্রিং-এর দৈর্ঘ্য ঠিক ৫ হয়, তাহলে
পিজনহোল প্রিন্সিপল কি নিশ্চিতভাবে একটি পুনরাবৃত্ত স্টেট গ্যারান্টি দেয়? দৈর্ঘ্য ৪ হলে কী হবে?
দৈর্ঘ্য ৫ (অর্থাৎ $|s|=p=5$): হ্যাঁ, গ্যারান্টিড। ৫টি ট্রানজিশন মানে ৬টি স্টেট ভিজিট (শুরুরটাসহ), কিন্তু মাত্র ৫টি সম্ভাব্য স্টেট — পিজনহোল অনুযায়ী একটি পুনরাবৃত্তি অবশ্যই ঘটবে। দৈর্ঘ্য ৪ ($|s|=4 < p=5$): না, গ্যারান্টিড নয় — মাত্র ৫টি স্টেট ভিজিট (৪ ট্রানজিশন + শুরুরটা), যা ঠিক স্টেট-সংখ্যার সমান, তাই তত্ত্বগতভাবে সবগুলোই ভিন্ন স্টেট হতে পারে, কোনো পুনরাবৃত্তি ছাড়াই। এই কারণেই লেমার শর্তে স্পষ্টভাবে $|s| \geq p$ (শুধু $>$ নয়, $\geq$-ও) বলা আছে।
-
পরীক্ষা করুন: উপরের কোড সেলে
test_string-এর বদলে একটি ভিন্ন, লম্বা বাইনারি স্ট্রিং (যেমন"0010110") ব্যবহার করে Run চাপুন — নতুন split এবং নতুন $xy^iz$ ফলাফলগুলো কি এখনও সব মূল স্ট্রিং-এর accept/reject অবস্থার সাথে সঙ্গতিপূর্ণ থাকে?হ্যাঁ, থাকা উচিত — এবং সবসময় থাকবে, যেকোনো দৈর্ঘ্য $\geq p$ স্ট্রিং-এর জন্যই, কারণ প্রমাণের যুক্তি (স্টেট-পুনরাবৃত্তি) কোনো নির্দিষ্ট স্ট্রিং-এর বৈশিষ্ট্যের উপর নির্ভর করে না।
"0010110"-এ (দৈর্ঘ্য ৭, ১-এর সংখ্যা ৩টি, বিজোড়) DFA q0→q0→q0→q1→q1→q1→q0→q1 পথে চলে — প্রথম ২ ট্রানজিশনেই q0 দুইবার ভিজিট হয় (x='', y='00', z='10110'), এবং $xy^iz$-এর প্রতিটিই ১-এর সংখ্যা অপরিবর্তিত রেখে শুধু 0-এর সংখ্যা বদলায় — তাই সব $i$-এর জন্যই reject-ই থেকে যায়, মূল স্ট্রিং-এর মতোই।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ পরবর্তী পাঠ — এই পাম্পিং লেমা কনট্রাপজিটিভ দিয়ে ব্যবহার করে ল্যাঙ্গুয়েজ নন-রেগুলার প্রমাণ করা শেখাবে।
- L06 · DFA — ফরমাল ডেফিনিশন পূর্ববর্তী ভিত্তি এই পাঠের DFA ক্লাস ও "even number of 1s" উদাহরণ সরাসরি সেই পাঠ থেকে পুনর্ব্যবহৃত।
- Discrete Mathematics কোর্স সহোদর কোর্স পিজনহোল প্রিন্সিপলের ফরমাল প্রমাণ ও কম্বিনেটোরিক্সের অন্যান্য মৌলিক ফলাফল সেই কোর্সে।
- সব 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 — সব এক জায়গায়।