পাঠ ৩৮ · ৫৬-এর মধ্যে · মডিউল ৯
Home / Courses / Formal Language & Automata Theory / Theory of Computation / ডিসাইডেবিলিটি

ডিসাইডেবল বনাম টুরিং-রিকগনাইজেবল ল্যাঙ্গুয়েজ

Decidable vs Turing-recognizable languages
৮ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ডিসাইডেবল (recursive) ও টুরিং-রিকগনাইজেবল (recursively enumerable) ভাষার ফরমাল, নির্ভুল সংজ্ঞা
  • co-recognizable ধারণা এবং এটি recognizable থেকে ঠিক কীভাবে আলাদা
  • "L ডিসাইডেবল $\iff$ L ও $\overline{L}$ উভয়ই recognizable" থিওরেমের সম্পূর্ণ, উভয়-দিকের প্রমাণ
  • ইন্টারলিভড সিমুলেশন টেকনিক — কীভাবে দুটো recognizer একসাথে চালিয়ে একটি নিশ্চিত-হল্টিং ডিসাইডার বানানো যায়
  • Python দিয়ে একটি real, working interleaved_simulation — যা L এবং তার complement-এর জন্য দুটো জেনারেটর-স্টাইল রিকগনাইজার পালা করে চালিয়ে সবসময় সঠিকভাবে হল্ট করে

১ · পুনরালোচনা ও নতুন পরিভাষা

L33-এ আমরা দেখেছিলাম টুরিং মেশিনের দুটো আচরণ — রিকগনাইজ করা আর ডিসাইড করা। এই মডিউলের (M9) পুরো বাকি অংশ এই পার্থক্যের উপরই দাঁড়িয়ে, তাই আরেকবার নির্ভুলভাবে বলা যাক —

  • ডিসাইডেবলDecidable (recursive)একটি TM প্রতিটি সম্ভাব্য ইনপুটে হল্ট করে, সঠিকভাবে accept বা reject করে (recursive) ভাষা: এমন একটি TM আছে যা প্রতিটি ইনপুটে হল্ট করে — সদস্য স্ট্রিং accept করে, অ-সদস্য স্ট্রিং সরাসরি reject করে (লুপ করে না)।
  • টুরিং-রিকগনাইজেবলTuring-recognizable (recursively enumerable)একটি TM সব YES-ইনস্ট্যান্সে accept করে হল্ট করে, কিন্তু NO-ইনস্ট্যান্সে লুপ করতেও পারে (recursively enumerable) ভাষা: এমন একটি TM আছে যা সদস্য স্ট্রিং-এ accept করে হল্ট করে, কিন্তু অ-সদস্য স্ট্রিং-এ হয় reject করে, নয়তো চিরকাল লুপ করতে পারে।
  • Co-recognizableCo-recognizableL-এর complement, $\overline{L}$, যদি Turing-recognizable হয় ভাষা: $L$-এর complement, $\overline{L}$, যদি Turing-recognizable হয় — অর্থাৎ, "L-এ নেই" প্রশ্নটা recognize করা যায়।

L33-এর গুরুত্বপূর্ণ, প্রায়ই-ভুল-বোঝা অসামঞ্জস্যটা আবার জোর দিয়ে বলা দরকার — প্রতিটি ডিসাইডেবল ভাষা recognizable (একটি decider নিজেই একটি recognizer, শুধু "সবসময় হল্ট করে" এই বাড়তি গ্যারান্টিসহ) — কিন্তু উল্টোটা সত্য নয় — কিছু ভাষা recognizable হয়েও ডিসাইডেবল নয় (এমন একটি recognizer আছে, কিন্তু সেটি অন্তত কিছু non-member স্ট্রিং-এ চিরকাল লুপ করে — কোনোভাবেই সেই recognizer-কে decider-এ রূপান্তর করা যায় না)। M9/L39-এর হল্টিং প্রবলেম হবে এই ফাঁকের সবচেয়ে বিখ্যাত উদাহরণ।

ডিসাইডেবল
সব ইনপুটে হল্ট করে সঠিক accept/reject — সবচেয়ে শক্তিশালী গ্যারান্টি।
রিকগনাইজেবল
সব YES-এ accept করে হল্ট করে, কিন্তু NO-তে লুপ করতেও পারে।
Co-recognizable
$\overline{L}$ (complement) recognizable — "না" উত্তরগুলো recognize করা যায়।

২ · মূল থিওরেম: L ডিসাইডেবল $\iff$ L ও $\overline{L}$ উভয়ই recognizable

এই থিওরেমটি সত্যিই সুন্দর, কারণ এর প্রমাণ কনস্ট্রাক্টিভ — শুধু দাবি করে না, বরং সরাসরি দেখায় কীভাবে decider বানাতে হয়।

($\Rightarrow$) সহজ দিক: যদি $L$ ডিসাইডেবল হয়, তাহলে তার decider-এর accept/reject সিদ্ধান্তটা উল্টে দিলেই (accept বদলে reject, reject বদলে accept) সরাসরি $\overline{L}$-এর একটি decider (তাই recognizer-ও) পাওয়া যায় — তুচ্ছ।

($\Leftarrow$) গুরুত্বপূর্ণ দিক: ধরা যাক $L$-এর একটি recognizer $M_1$ আছে, এবং $\overline{L}$-এর একটি recognizer $M_2$ আছে। এখন একই ইনপুট $w$-এর উপর $M_1$ ও $M_2$-কে একই সাথে চালাই — একটি স্টেপ $M_1$-এর, একটি স্টেপ $M_2$-এর, এভাবে পালা করে (ইন্টারলিভড)। যেহেতু $w$ অবশ্যই $L$-এ আছে অথবা $\overline{L}$-এ আছে (একইসাথে দুটোতেই নয়, কখনোই কোনোটাতেই না-ও নয়) — $M_1, M_2$-এর মধ্যে ঠিক একজন নিশ্চিতভাবে কোনো-না-কোনো সসীম সংখ্যক স্টেপের পর accept করবে। যে আগে accept করে, তার সিদ্ধান্তই চূড়ান্ত উত্তর — এই ইন্টারলিভড সিমুলেশনটাই $L$-এর একটি decider, কারণ এটি সবসময় হল্ট করে (দুটোর মধ্যে একটা তো নিশ্চিতভাবে accept করবেই)।

M1 -- L-এর recognizer M2 -- L̄-এর recognizer স্টেপ 1 (M1) → স্টেপ 1 (M2) → স্টেপ 2 (M1) → স্টেপ 2 (M2) → ... যেই আগে accept করে সেটাই চূড়ান্ত, নিশ্চিত সিদ্ধান্ত w হয় L-এ, নয় L̄-এ -- তাই M1, M2-এর একজন অবশ্যই কোনো-না-কোনো সসীম স্টেপে accept করবেই
ইন্টারলিভড সিমুলেশন — L ও তার complement-এর দুটো recognizer একসাথে, পালা করে চালানো হয়; একজন নিশ্চিতভাবে accept করবেই, তাই এই সমন্বিত মেশিনটি সবসময় হল্ট করে — একটি genuine decider।

৩ · Python-এ ইন্টারলিভড সিমুলেশন

নিচের কোড সেলে দুটো ভিন্ন জেনারেটর-স্টাইল রিকগনাইজার বানানো হচ্ছে — একটি $L$ = "স্ট্রিং-এ অন্তত একটি 1 আছে"-এর জন্য, আরেকটি $\overline{L}$ = "স্ট্রিং সম্পূর্ণ শূন্য দিয়ে গঠিত"-এর জন্য। প্রতিটি রিকগনাইজার একটি স্টেপ-বাই-স্টেপ জেনারেটর (ব্ল্যাক-বক্স ফাংশন কল নয়) — যদি নিজের ভাষায় সদস্যপদ প্রমাণ করতে ব্যর্থ হয়, সেটি সত্যিকারের একটি recognizer-এর মতোই চিরকাল 'continue' yield করতে থাকে (কখনো হল্ট করে না) — শুধু "সদস্য" পেলেই 'accept' yield করে থামে। interleaved_simulation এই দুটোকে পালা করে এক-এক স্টেপ চালিয়ে, যেটাই আগে accept করে সেটার উত্তর রিটার্ন করে।

Python
def contains_one_recognizer(s):
    """L = 'স্ট্রিং-এ অন্তত একটি 1 আছে' -- একটি recognizer, জেনারেটর হিসেবে।
    '1' না পাওয়া গেলে চিরকাল 'continue' yield করে -- কখনো হল্ট করে না।"""
    def gen():
        i = 0
        while True:
            if i < len(s):
                if s[i] == '1':
                    yield 'accept'
                    return
                i += 1
                yield 'continue'
            else:
                yield 'continue'   # '1' পাওয়া যায়নি -- চিরকাল লুপ (কখনো হল্ট করে না)
    return gen()


def all_zeros_recognizer(s):
    """L̄ = 'স্ট্রিং সম্পূর্ণ শূন্য দিয়ে গঠিত' -- এটাও একটি recognizer।
    কোনো non-zero পেলে চিরকাল লুপ করে -- কখনো হল্ট করে না।"""
    def gen():
        i, stuck = 0, False
        while True:
            if stuck:
                yield 'continue'
                continue
            if i >= len(s):
                yield 'accept'
                return
            if s[i] != '0':
                stuck = True        # non-zero পাওয়া গেছে -- এই recognizer আর কখনো accept করবে না
                yield 'continue'
                continue
            i += 1
            yield 'continue'
    return gen()


def interleaved_simulation(gen1, gen2, max_total_steps=2000):
    """L38-থিওরেমের কনস্ট্রাক্টিভ প্রমাণ -- L আর L̄-এর দুটো recognizer-কে একটি করে
    স্টেপ পালা করে চালানো হয় -- যেটা আগে accept করে, সেটাই চূড়ান্ত সিদ্ধান্ত।"""
    steps = 0
    while steps < max_total_steps:
        r1 = next(gen1)
        steps += 1
        if r1 == 'accept':
            return 'L', steps
        if steps >= max_total_steps:
            break
        r2 = next(gen2)
        steps += 1
        if r2 == 'accept':
            return 'complement of L', steps
    return 'timeout', steps


# --- যাচাই: প্রতিটি স্ট্রিং-এ ডিসাইডার নিশ্চিতভাবে হল্ট করে ও সঠিক উত্তর দেয় ---
tests = ["", "0", "1", "000", "111", "0001", "1000", "00000001", "0000000"]
print(f"{'w':12s} | {'সিদ্ধান্ত':17s} | স্টেপ | প্রত্যাশিত মিলছে?")
print("-" * 58)
all_correct = True
for s in tests:
    expected = 'L' if '1' in s else 'complement of L'
    which, steps = interleaved_simulation(contains_one_recognizer(s), all_zeros_recognizer(s))
    correct = (which == expected)
    all_correct = all_correct and correct
    print(f"{s!r:12s} | {which:17s} | {steps:4d}  | {'হ্যাঁ' if correct else 'না'}")

print(f"\nপ্রতিটি টেস্টে ডিসাইডার হল্ট করেছে ও সঠিক উত্তর দিয়েছে: {all_correct}")

    
লক্ষ্য করুন খালি স্ট্রিং ""-এর ফলাফল — এতে কোনো 1 নেই, তাই এটি contains_one_recognizer-এর কাছে কখনোই accept হবে না (এই recognizer চিরকাল লুপ করত, যদি একাই চালানো হতো) — কিন্তু all_zeros_recognizer তাৎক্ষণিকভাবে accept করে (খালি স্ট্রিং vacuously "সম্পূর্ণ শূন্য")। ইন্টারলিভড সিমুলেশন এই দ্বিতীয়টার accept-এর উপর নির্ভর করেই সঠিকভাবে হল্ট করে — এটাই থিওরেমের মূল কথার একটি বাস্তব, কংক্রিট প্রদর্শন।
মূল কথা · Key takeaway

ডিসাইডেবল বনাম রিকগনাইজেবল-এর পার্থক্যটাই M9-এর হৃদয়। যেখানে L ও তার complement দুটোই recognizable, সেখানে ইন্টারলিভড সিমুলেশনের এই এক টেকনিকেই একটি পূর্ণাঙ্গ decider পাওয়া যায়। কিন্তু L39-এ আমরা দেখব একটি ভাষা (HALT) আছে যেখানে এই টেকনিকটাও কাজ করে না — কারণ $\overline{HALT}$ আদৌ recognizable-ই নয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ প্রতিটি ডিসাইডেবল ভাষা কেন recognizable, কিন্তু উল্টোটা সবসময় সত্য নয় কেন?

একটি decider (সব ইনপুটে হল্ট করে, সঠিক accept/reject দেয়) নিজেই একটি বৈধ recognizer — শুধু "সব ইনপুটে হল্ট করে" এই বাড়তি গ্যারান্টিসহ, তাই decidable $\Rightarrow$ recognizable তুচ্ছভাবে সত্য। কিন্তু উল্টোদিকে, একটি recognizer শুধু নিশ্চিত করে YES-ইনস্ট্যান্সে accept হবে — NO-ইনস্ট্যান্সে সে চিরকাল লুপ করতেও পারে, কখনো "reject" বলে হল্ট না করেই। এই গ্যারান্টির অভাবের কারণেই recognizable হয়েও ডিসাইডেবল না হওয়া সম্ভব — L39-এ HALT ভাষা এর প্রমাণিত উদাহরণ হবে।

প্র ০২ co-recognizable মানে কী, এবং এটি recognizable থেকে কীভাবে আলাদা?

একটি ভাষা $L$ co-recognizable মানে $L$ নিজে recognizable না-ও হতে পারে, কিন্তু তার complement $\overline{L}$ recognizable — অর্থাৎ "এই স্ট্রিংটা $L$-এ নেই" এই প্রশ্নটা recognize করা যায়। এটা recognizable-এর থেকে সম্পূর্ণ ভিন্ন ধারণা — একটি ভাষা recognizable, co-recognizable, দুটোই, বা কোনোটাই না হতে পারে। যখন একটি ভাষা উভয়ই হয় (recognizable এবং co-recognizable), তখনই এই পাঠের থিওরেম অনুযায়ী সেটি ডিসাইডেবল হয়ে যায়।

প্র ০৩ কেন ইন্টারলিভড সিমুলেশন সবসময় হল্ট করার গ্যারান্টি দেয়?

কারণ যেকোনো ইনপুট $w$ অবশ্যই $L$-এ আছে অথবা $\overline{L}$-এ আছে — এই দুটোর বাইরে তৃতীয় কোনো সম্ভাবনা নেই, এবং দুটো একসাথেও সম্ভব নয় (L ও $\overline{L}$ সংজ্ঞানুসারেই disjoint, এবং তাদের ইউনিয়ন সম্পূর্ণ $\Sigma^*$)। যেহেতু $M_1$ ($L$-এর recognizer) $w \in L$ হলে নিশ্চিতভাবে সসীম স্টেপে accept করবে, আর $M_2$ ($\overline{L}$-এর recognizer) $w \notin L$ হলে নিশ্চিতভাবে সসীম স্টেপে accept করবে — এই দুটোর মধ্যে ঠিক একজন সবসময় নিশ্চিতভাবে accept করবেই, তাই ইন্টারলিভড সিমুলেশন কখনো অনির্দিষ্টকালের জন্য আটকে থাকতে পারে না।

অনুশীলন

  1. চিন্তা করুন: যদি $L$ ও $\overline{L}$ উভয়ই শুধুমাত্র recognizable হয় (কেউই decidable না) — তাহলে ইন্টারলিভড সিমুলেশন কী সমস্যায় পড়বে? এটা কি তখনও একটি decider হবে?

    এই পরিস্থিতি আসলে ঘটতেই পারে না — এটাই এই পাঠের থিওরেমের সবচেয়ে গুরুত্বপূর্ণ পয়েন্ট। যদি $L$ ও $\overline{L}$ উভয়ই recognizable হয়, তাহলে থিওরেম অনুযায়ী $L$ (এবং তাই $\overline{L}$-ও) স্বয়ংক্রিয়ভাবে ডিসাইডেবল হয়ে যায় — কারণ ইন্টারলিভড সিমুলেশন নিজেই একটি decider গঠন করে। তাই "L ও L̄ উভয়ই recognizable কিন্তু L decidable নয়" — এমন কোনো ভাষা থাকতে পারে না, এটা একটি logical impossibility।

  2. পরীক্ষা করুন: কোড সেলে tests লিস্টে আপনার নিজের একটি নতুন টেস্ট স্ট্রিং (যেমন "00100") যোগ করে Run চেপে দেখুন — interleaved_simulation সঠিক ভাষা identify করে কি না, এবং কত স্টেপে করে তা লক্ষ্য করুন।

    "00100"-এ একটি 1 আছে (তৃতীয় পজিশনে), তাই এটি $L$-এ — contains_one_recognizer-এর ৩ নম্বর স্টেপেই (i=0,1,2 পার হয়ে '1' পেয়ে) accept হবে। যেহেতু M1 প্রথম স্টেপ থেকেই শুরু করছে এবং M2-এর আগেই এগিয়ে যাচ্ছে, ইন্টারলিভড সিমুলেশন 'L' রিটার্ন করবে, মোট স্টেপ সংখ্যা প্রায় ৫ (M1-এর ৩টি স্টেপ + মাঝে M2-এর ২টি স্টেপ, ইন্টারলিভিং-এর ধরন অনুযায়ী)।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
চার্চ-টুরিং থিসিস ও ইউনিভার্সাল টুরিং মেশিন