DFA — ফরমাল ডেফিনিশন ও ল্যাঙ্গুয়েজ অ্যাকসেপ্টেন্স
এই পাঠে যা শিখবেন
- DFA-এর ফরমাল ৫-টাপল সংজ্ঞা এবং প্রতিটি উপাদানের অর্থ
- Extended transition function $\hat\delta$ — ইনডাকটিভ সংজ্ঞা
- DFA-এর ভাষা $L(M)$ ও regular ভাষার সংজ্ঞা
- টোটালিটি (totality) — কেন এটি DFA-এর সংজ্ঞাগত অংশ, শুধু একটি ইমপ্লিমেন্টেশন ডিটেইল নয়
- Python-এ একটি real, formal
DFAক্লাস — দুটি ভিন্ন ভাষার জন্য দুটি DFA
১ · DFA-এর ফরমাল সংজ্ঞা
L01-এ আমরা একটি অ্যাডহক DFA প্রিভিউ দেখেছিলাম (dict দিয়ে transitions)। এখন M2-এর প্রথম পাঠে সেটিকে ফরমালাইজ করছি। DFADeterministic Finite Automatonএকটি ৫-টাপল (Q, Σ, δ, q0, F) যেখানে δ প্রতিটি (state, symbol) জোড়ার জন্য ঠিক একটি (সবসময় সংজ্ঞায়িত) পরবর্তী স্টেট দেয়। (Deterministic Finite Automaton) ফরমালি একটি ৫-টাপল —
$$M = (Q, \Sigma, \delta, q_0, F)$$
- $Q$ — স্টেটের একটি ফাইনাইট সেট
- $\Sigma$ — ইনপুট আলফাবেট (L02)
- $\delta: Q \times \Sigma \to Q$ — ট্রানজিশন ফাংশন, একটি টোটাল ফাংশন — প্রতিটি $(q, a) \in Q \times \Sigma$ জোড়ার জন্য সংজ্ঞায়িত ("ডিটারমিনিস্টিক" মানে ঠিক একটি, সবসময়-সংজ্ঞায়িত পরবর্তী স্টেট)
- $q_0 \in Q$ — স্টার্ট স্টেট
- $F \subseteq Q$ — accepting/final স্টেটের সেট
$\delta$ একটি টোটাল ফাংশন হওয়া মানে — DFA কখনো "আটকে যায় না।" প্রতিটি স্টেট থেকে প্রতিটি সিম্বলের জন্য একটি নির্দিষ্ট পরবর্তী স্টেট আছে (এমনকি "রিজেক্ট"-এর জন্যও একটি সিঙ্ক/ট্র্যাপ স্টেট থাকতে পারে)। এই টোটালিটিই M3/L12-এর complement closure প্রমাণের ভিত্তি — accept/non-accept স্টেট অদল-বদল করলেই একটি সম্পূর্ণ, বৈধ DFA পাওয়া যায়, শুধু তখনই যখন মূল DFA টোটাল।
২ · Extended Transition Function $\hat\delta$
$\delta$ শুধু একটি সিম্বল প্রসেস করে। পুরো একটি স্ট্রিং প্রসেস করতে দরকার extended transition function $\hat\delta$Extended Transition Functionδ-কে পুরো স্ট্রিং প্রসেস করার জন্য সম্প্রসারিত রূপ, স্ট্রিং length-এর উপর স্ট্রাকচারাল ইনডাকশনে সংজ্ঞায়িত। — L03-এর স্ট্রাকচারাল ইনডাকশন কাঠামো অনুযায়ী ঠিক সংজ্ঞায়িত —
$$\hat\delta(q, \varepsilon) = q \qquad \text{(base case)}$$ $$\hat\delta(q, wa) = \delta(\hat\delta(q, w), a) \qquad \text{(inductive case)}$$
অর্থাৎ — স্ট্রিং $w$-এর শেষ সিম্বল $a$ বাদে বাকিটুকু ($w$) প্রসেস করে যে স্টেটে পৌঁছানো যায়, সেখান থেকে $a$ পড়ে এক ধাপ আরও এগোনো। বারবার এই সংজ্ঞা প্রয়োগ করলে পুরো স্ট্রিং, সিম্বল-বাই-সিম্বল, প্রসেস হয়ে যায়।
৩ · DFA-এর ল্যাঙ্গুয়েজ ও Regular ভাষা
একটি DFA $M$-এর ল্যাঙ্গুয়েজ —
$$L(M) = \{w \in \Sigma^* : \hat\delta(q_0, w) \in F\}$$
অর্থাৎ, $q_0$ থেকে শুরু করে $w$ প্রসেস করলে যে স্ট্রিংগুলো একটি accept স্টেটে গিয়ে শেষ হয়, তাদের সবার সেট। এখন L05-এর "সমস্যা = ল্যাঙ্গুয়েজ" রিফ্রেমিং মনে করুন — $L(M)$ হলো ঠিক সেই ডিসিশন প্রবলেম যা $M$ "সমাধান করে।" একটি ভাষা $L$-কে regular বলা হয় যদি কোনো DFA $M$-এর জন্য $L(M) = L$ হয় — এটিই M2-M3-এর কেন্দ্রীয় সংজ্ঞা, যার উপর বাকি সব প্রপার্টি (M3), সমতুল্যতা (M2/L08, L11) ও সীমাবদ্ধতা (M3/L13-L14) প্রমাণিত হবে।
৪ · Python-এ একটি ফরমাল DFA ক্লাস
নিচের কোড সেলে ৫-টাপল সংজ্ঞা হুবহু মিলিয়ে একটি DFA ক্লাস — টোটালিটি কনস্ট্রাক্টরেই যাচাই করা হচ্ছে (কনস্ট্রাকশনের সময় assert করা হয় প্রতিটি (state, symbol) জোড়ার জন্য একটি ট্রানজিশন সংজ্ঞায়িত আছে)। L01-এর "জোড়-সংখ্যক 1" ভাষাটি এখন ফরমালি এই ক্লাস দিয়ে বানানো হচ্ছে, এবং একটি সম্পূর্ণ ভিন্ন দ্বিতীয় ভাষা — "01 দিয়ে শেষ হওয়া স্ট্রিং।"
class DFA:
"""(Q, Sigma, delta, q0, F) -- purno formal DFA definition"""
def __init__(self, states, alphabet, transitions, start_state, accept_states):
self.states = set(states)
self.alphabet = set(alphabet)
self.transitions = transitions # dict: (state, symbol) -> state
self.start_state = start_state
self.accept_states = set(accept_states)
# totality jachai -- delta protita (state, symbol) jorar jonno songjayito thakte hobe
for q in self.states:
for a in self.alphabet:
if (q, a) not in self.transitions:
raise ValueError(f"osomponno DFA -- delta({q!r},{a!r}) songjayito nei (totality longhito)")
def extended_delta(self, string):
"""delta-hat(q0, w) gonona kore -- base case: delta-hat(q,e)=q,
inductive case: delta-hat(q,wa)=delta(delta-hat(q,w),a)"""
state = self.start_state
for symbol in string:
if symbol not in self.alphabet:
raise ValueError(f"'{symbol}' ei DFA-r alphabet-e nei")
state = self.transitions[(state, symbol)]
return state
def accepts(self, string):
return self.extended_delta(string) in self.accept_states
# L01-er 'jor-shonkhok 1' bhasha, ekhon formal DFA class diye
even_ones_dfa = DFA(
states={'q0', 'q1'},
alphabet={'0', '1'},
transitions={
('q0', '0'): 'q0', ('q0', '1'): 'q1',
('q1', '0'): 'q1', ('q1', '1'): 'q0',
},
start_state='q0',
accept_states={'q0'},
)
# ekti notun, alada bhasha -- '01' die shesh hoya string
ends_in_01_dfa = DFA(
states={'r0', 'r1', 'r2'},
alphabet={'0', '1'},
transitions={
('r0', '0'): 'r1', ('r0', '1'): 'r0',
('r1', '0'): 'r1', ('r1', '1'): 'r2',
('r2', '0'): 'r1', ('r2', '1'): 'r0',
},
start_state='r0',
accept_states={'r2'},
)
test_strings = ["", "0", "1", "01", "10", "0101", "1100", "111", "010101"]
print(f"{'w':10s} | even-1s DFA | ends-in-01 DFA")
print("-" * 45)
for w in test_strings:
r1 = even_ones_dfa.accepts(w)
r2 = ends_in_01_dfa.accepts(w)
print(f"{w!r:10s} | {str(r1):11s} | {r2}")
ends_in_01_dfa-এর তিনটি স্টেট আসলে "এখন পর্যন্ত সাফিক্স কী দেখা গেছে" ট্র্যাক করছে — $r_0$ = "শেষ সিম্বল 1 বা কিছুই দেখা যায়নি", $r_1$ = "শেষ সিম্বল 0", $r_2$ = "শেষ দুই সিম্বল ঠিক 01" (accept)। এই "মেমরি" পুরোপুরি ফাইনাইট — DFA কখনো পুরো ইনপুট মনে রাখে না, শুধু এই তিনটির একটি স্টেটে থাকে।
DFA-এর ফরমাল সংজ্ঞা — ৫-টাপল, টোটাল $\delta$, ইনডাকটিভ $\hat\delta$, এবং $L(M)$ — এই পুরো M2-M3-এর ভাষা। পরের পাঠে (L07) আমরা $\delta$-কে nondeterministic করে NFA দেখব — একই আকৃতি, কিন্তু একটি মৌলিকভাবে ভিন্ন acceptance নিয়ম।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ $\delta$ কেন একটি টোটাল ফাংশন হতে হয় — একটি partial ফাংশন (কিছু জোড়ার জন্য undefined) দিয়ে DFA সংজ্ঞায়িত করলে কী সমস্যা হতো?
যদি $\delta$ partial হতো, তাহলে কিছু ইনপুট স্ট্রিং প্রসেস করার সময় DFA "আটকে যেতে" পারত (কোনো সংজ্ঞায়িত পরবর্তী স্টেট না থাকায়) — এবং তখন সেই স্ট্রিং-এর জন্য accept/reject কোনোটাই বলা সম্ভব হতো না, যা L05-এর ডিসিশন-প্রবলেম ফ্রেমওয়ার্ককে ভেঙে দিত (প্রতিটি ইনপুটের জন্য একটি নির্দিষ্ট YES/NO উত্তর থাকতেই হবে)। টোটালিটি নিশ্চিত করে প্রতিটি স্ট্রিং $w \in \Sigma^*$-এর জন্য $\hat\delta(q_0,w)$ সবসময় সংজ্ঞায়িত এবং একটি নির্দিষ্ট ফলাফল দেয়। এছাড়াও M3/L12-এর কমপ্লিমেন্ট ক্লোজার প্রুফ (accept/non-accept অদল-বদল) সরাসরি টোটালিটির উপর নির্ভর করে।
প্র ০২ $\hat\delta$-এর সংজ্ঞা কেন স্ট্রিং-এর শেষ সিম্বলের উপর ইনডাকশন করে ($wa$), শুরুর সিম্বলের উপর নয় ($aw$)?
কারণ এটি ঠিক DFA প্রসেসিং-এর দিকের সাথে মেলে — একটি DFA বাম থেকে ডানে, এক-এক করে সিম্বল পড়ে। "$w$ প্রসেস করার পর $a$ পড়া" ($\delta(\hat\delta(q,w),a)$) মানে হলো — প্রথমে $w$-এর প্রভাব হিসাব করে ফেলো (যা নিজেই ছোট, তাই ইনডাকটিভ হাইপোথিসিস প্রযোজ্য), তারপর সবশেষে নতুন সিম্বল $a$-এর একটি মাত্র $\delta$ কল প্রয়োগ করো। এটি L03-এর স্ট্রাকচারাল ইনডাকশনের ঠিক সেই প্যাটার্ন ($w \to wa$) অনুসরণ করে, যা "একটি সিম্বল বাড়ানো"-কে রিকার্সিভ কেস হিসেবে ব্যবহার করে।
প্র ০৩
উপরের কোড সেলে ends_in_01_dfa স্ট্রিং "0101" accept করে কিন্তু "1100" করে না কেন — হাতে ট্রেস করে দেখান।
"0101": $r_0 \xrightarrow{0} r_1 \xrightarrow{1} r_2 \xrightarrow{0} r_1 \xrightarrow{1} r_2$ — শেষ স্টেট $r_2$, যা accept স্টেট, কারণ স্ট্রিংটি সত্যিই "01" দিয়ে শেষ হয়েছে। "1100": $r_0 \xrightarrow{1} r_0 \xrightarrow{1} r_0 \xrightarrow{0} r_1 \xrightarrow{0} r_1$ — শেষ স্টেট $r_1$ (accept নয়), কারণ স্ট্রিংটি "00" দিয়ে শেষ হয়েছে, "01" দিয়ে নয়। উভয় ট্রেসই দেখায় DFA ঠিক সঠিকভাবে "শেষ দুই সিম্বল কী" ট্র্যাক করছে, পুরো স্ট্রিং না মনে রেখেই।
অনুশীলন
-
হাতে করুন: $\hat\delta$-এর ইনডাকটিভ সংজ্ঞা ব্যবহার করে হাতে দেখান
even_ones_dfa-এর জন্য $\hat\delta(q_0, \text{"101"})$ কী — ধাপে ধাপে $\hat\delta(q_0,\varepsilon)$, $\hat\delta(q_0,\text{"1"})$, $\hat\delta(q_0,\text{"10"})$, $\hat\delta(q_0,\text{"101"})$ লিখুন।$\hat\delta(q_0,\varepsilon)=q_0$ (base case)। $\hat\delta(q_0,\text{"1"}) = \delta(\hat\delta(q_0,\varepsilon),1) = \delta(q_0,1) = q_1$। $\hat\delta(q_0,\text{"10"}) = \delta(\hat\delta(q_0,\text{"1"}),0) = \delta(q_1,0) = q_1$। $\hat\delta(q_0,\text{"101"}) = \delta(\hat\delta(q_0,\text{"10"}),1) = \delta(q_1,1) = q_0$। যেহেতু $q_0 \in F$, "101" accept হয় — এবং সত্যিই "101"-এ দুটি '1' আছে (জোড়), তাই এটি সঠিক।
-
নতুন DFA বানান: উপরের কোড সেলের প্যাটার্ন অনুসরণ করে একটি তৃতীয় DFA বানান — "$\Sigma=\{0,1\}$-এর উপর length ঠিক ৩ জোড় (multiple of 3)"-এর ভাষার জন্য নয়, বরং সহজ একটি ভাষা: "স্ট্রিং-এ অন্তত একটি '1' আছে" — এটির জন্য ন্যূনতম কয়টি স্টেট লাগবে?
ঠিক ২টি স্টেট যথেষ্ট — $s_0$ (এখনো কোনো '1' দেখা যায়নি, non-accept) এবং $s_1$ (অন্তত একটি '1' দেখা গেছে, accept, এবং একবার এখানে পৌঁছালে সবসময় এখানেই থাকবে)। ট্রানজিশন: $\delta(s_0,0)=s_0$, $\delta(s_0,1)=s_1$, $\delta(s_1,0)=s_1$, $\delta(s_1,1)=s_1$ — $s_1$ একটি "স্টিকি" accept স্টেট, যা দেখায় কিছু ভাষার জন্য খুব ছোট, সরল DFA যথেষ্ট।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ পরবর্তী পাঠ — NFA: ফরমাল ডেফিনিশন — এখনই পড়া যাবে।
- Finite Automata — NFA to DFA PLC L18 সেই কোর্সে এই একই DFA/NFA মেশিন ব্যবহৃত হয় বাস্তবে লেক্সার বানাতে — এখানে তার পেছনের ফরমাল সংজ্ঞা ও প্রমাণে ফোকাস করা হচ্ছে।
- সব 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 — সব এক জায়গায়।