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

হল্টিং প্রবলেম

The halting problem
৯ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

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

  • HALT ভাষার ফরমাল সংজ্ঞা, L37-এর এনকোডিং নোটেশন ব্যবহার করে
  • HALT আনডিসাইডেবল — এই থিওরেমের সম্পূর্ণ, নির্ভুল, ধাপে-ধাপে প্রমাণ (ডায়াগোনালাইজেশন/কনট্রাডিকশন)
  • D-এর স্ব-নির্দেশনামূলক (self-referential) নির্মাণ কীভাবে ঠিক একটি অমীমাংসিত দ্বন্দ্ব তৈরি করে
  • L02-এর Cantor's diagonal argument-এর সাথে এই প্রমাণের গঠনগত সাদৃশ্য
  • Python দিয়ে diagonalization-এর self-contradiction প্যাটার্নের একটি নিয়ন্ত্রিত, কংক্রিট ইলাস্ট্রেশন — একটি real universal halt-checker নয় (যা অসম্ভব), বরং প্রমাণের যুক্তি-কাঠামোটাই স্পষ্ট করে দেখানো

১ · HALT ভাষার সংজ্ঞা

L37-এর এনকোডিং নোটেশন $\langle \cdot \rangle$ ব্যবহার করে, ফরমালি —

$$HALT = \{\langle M, w \rangle : M \text{ একটি টুরিং মেশিন এবং } M \text{ ইনপুট } w\text{-এর উপর হল্ট করে}\}$$

এটি কম্পিউটার সায়েন্সের সবচেয়ে বিখ্যাত আনডিসাইডেবিলিটিUndecidabilityএমন একটি প্রশ্ন যা কোনো অ্যালগরিদম দিয়েই সবসময় সঠিকভাবে উত্তর দেওয়া সম্ভব নয় ফলাফল। থিওরেম বলছে — কোনো TM-ই প্রতিটি সম্ভাব্য $(M, w)$ জোড়ার জন্য নির্ভুলভাবে "হল্ট করে" বা "চিরকাল লুপ করে" নির্ধারণ করতে পারে না।

২ · প্রমাণ — ডায়াগোনালাইজেশন ও কনট্রাডিকশন

এই প্রমাণটি সঠিকভাবে বোঝা সত্যিই গুরুত্বপূর্ণ — এটি এই কোর্সের সবচেয়ে বিখ্যাত, উদযাপিত যুক্তি। ধাপে ধাপে —

  1. ধরে নিন (contradiction-এর জন্য): HALT-এর একটি decider $H$ আছে — $H(\langle M, w \rangle)$ সবসময় হল্ট করে, এবং সঠিকভাবে "হল্ট করে" বা "চিরকাল লুপ করে" আউটপুট দেয়।
  2. একটি নতুন মেশিন $D$ বানান — ইনপুট $\langle M \rangle$ (একটি TM-এর নিজের বর্ণনা, স্ব-নির্দেশনামূলকভাবে) নিয়ে, $D$ চালায় $H(\langle M, M \rangle)$ — অর্থাৎ প্রশ্ন করে "$M$ কি নিজের নিজের বর্ণনার উপর হল্ট করে?" — যদি $H$ বলে "হল্ট করে," তাহলে $D$ ইচ্ছাকৃতভাবে চিরকাল লুপ করে; যদি $H$ বলে "চিরকাল লুপ করে," তাহলে $D$ ইচ্ছাকৃতভাবে হল্ট করে (accept করে) — অর্থাৎ, $D$-কে ঠিক $H$-এর predict-এর বিপরীত করার জন্য নির্মাণ করা হয়েছে।
  3. এখন জিজ্ঞাসা করুন — $D$ নিজের বর্ণনা $\langle D \rangle$-এর উপর কী করে?
    • যদি $D$, $\langle D \rangle$-এর উপর হল্ট করে — তাহলে $D$-এর নির্মাণ অনুযায়ী, $H(\langle D, D \rangle)$ অবশ্যই "চিরকাল লুপ করে" বলেছিল — কিন্তু $D$ তো হল্টই করল — $H$ ভুল ছিল।
    • যদি $D$, $\langle D \rangle$-এর উপর চিরকাল লুপ করে — তাহলে $D$-এর নির্মাণ অনুযায়ী, $H(\langle D, D \rangle)$ অবশ্যই "হল্ট করে" বলেছিল — কিন্তু $D$ তো লুপই করল — $H$ আবারও ভুল।
  4. উভয় ক্ষেত্রেই — $H$ ভুল উত্তর দেয় — এটি $H$ একটি সঠিক decider, এই অনুমানের সাথে সরাসরি কনট্রাডিকশন। তাই এমন কোনো $H$ থাকতে পারে না — HALT আনডিসাইডেবল। ∎
H(⟨D⟩,⟨D⟩) predict করে "halts" D-এর নির্মাণ: D লুপ করে "loops_forever" D-এর নির্মাণ: D হল্ট করে উভয় ক্ষেত্রেই D-এর প্রকৃত আচরণ H-এর predict-এর বিপরীত = কনট্রাডিকশন
H যা-ই predict করুক না কেন — D ঠিক তার বিপরীতটাই করার জন্য নির্মিত — তাই D-কে D-এর নিজের বর্ণনার উপর চালালে, H-এর predict কখনোই সঠিক হতে পারে না।

৩ · Cantor-এর ডায়াগোনাল আর্গুমেন্টের সাথে সংযোগ

L02-এ উল্লেখ করা হয়েছিল যে একটি অ্যালফাবেটের উপর সম্ভাব্য ল্যাঙ্গুয়েজের সংখ্যা অগণনীয় (uncountable), যদিও সম্ভাব্য প্রোগ্রামের সংখ্যা গণনীয় (countable) — Cantor-এর ডায়াগোনাল আর্গুমেন্ট ব্যবহার করে। এই প্রমাণটির গঠন সেই একই আর্গুমেন্টের সাথে গঠনগতভাবে অভিন্ন — $D$ বিশেষভাবে নির্মিত হয়েছে যাতে সে $H$-এর predict থেকে ঠিক ততটাই "ডিফার" করে, ঠিক যেভাবে Cantor-এর নির্মিত সেটটি তালিকার প্রতিটি সেট থেকে অন্তত একটি উপাদানে "ডিফার" করে (তার নিজের, "diagonal" এন্ট্রিতে) — দুটোই "নিজের বিপরীত এমন কিছু বানাও, যা যেকোনো প্রস্তাবিত সমাধানকে ব্যর্থ করে দেয়" — এই একই কৌশলের প্রয়োগ।

৪ · কোড সেল — ডায়াগোনালাইজেশনের যুক্তি-কাঠামো কংক্রিটভাবে দেখা

একটি স্পষ্টীকরণ জরুরি — নিচের কোড সেলে কোনো সত্যিকারের সাধারণ-উদ্দেশ্যে হল্ট-চেকার নেই (এমন কিছু বানানো অসম্ভব, এটাই তো এই প্রমাণের সিদ্ধান্ত)। বরং, hypothetical_halt_checker একটি ইচ্ছাকৃতভাবে স্টাব/ফেক ফাংশন — যা শুধু একটি hypothetical prediction ফেরত দেয়, যাতে diagonal_machine-এর স্ব-বৈসাদৃশ্যমূলক নির্মাণটা কংক্রিটভাবে চালিয়ে দেখানো যায়। কোড সেলটি উভয় সম্ভাব্য prediction-এর জন্যই (H "halts" বলুক বা "loops_forever" বলুক) দেখায় যে D-এর প্রকৃত আচরণ predict-কে মিথ্যা প্রমাণ করে — একটি সম্পূর্ণ, exhaustive contradiction।

Python
# H-এর অস্তিত্ব ধরে নিলে কী হয় তা কংক্রিটভাবে দেখানোর জন্য একটি স্টাব/ফেক checker।
# বাস্তবে এমন কোনো সত্যিকারের general-purpose H থাকতে পারে না -- এটাই এই প্রমাণের সিদ্ধান্ত।

def hypothetical_halt_checker(predicted_verdict, program_description, input_description):
    """(STUB, ফেক) -- হাইপোথেটিক্যালি ধরে নেওয়া H(<program_description>, <input_description>)
    -- ইলাস্ট্রেশনের জন্য predicted_verdict-ই ('halts' অথবা 'loops_forever') সরাসরি ফেরত
    দেয়, যাতে diagonalization-এর স্ব-বৈসাদৃশ্য কংক্রিটভাবে ধরা যায়।"""
    return predicted_verdict


def diagonal_machine(predicted_verdict, description_of_self):
    """D-এর নির্মাণ (এই পাঠের মূল কৌশল): H(⟨M⟩,⟨M⟩) 'halts' predict করলে D ইচ্ছাকৃতভাবে
    চিরকাল লুপ করে; 'loops_forever' predict করলে D ইচ্ছাকৃতভাবে হল্ট করে (accept)।"""
    checker_says = hypothetical_halt_checker(predicted_verdict, description_of_self, description_of_self)
    if checker_says == 'halts':
        return 'D চিরকাল লুপ করে (deliberately, D-এর নির্মাণ অনুযায়ী)'
    else:  # checker_says == 'loops_forever'
        return 'D হল্ট করে ও accept করে (deliberately, D-এর নির্মাণ অনুযায়ী)'


# --- এবার D-কে নিজের বর্ণনার উপর চালাই -- H যা-ই predict করুক না কেন ---
D_description = "⟨D⟩"  # D নিজের বর্ণনা ইনপুট হিসেবে নিচ্ছে -- স্ব-নির্দেশনা (self-reference)

print("প্রশ্ন: H(⟨D⟩, ⟨D⟩) কী predict করে -- এবং D আসলে কী করে?\n")

for hypothetical_prediction in ['halts', 'loops_forever']:
    actual_behavior = diagonal_machine(hypothetical_prediction, D_description)
    if hypothetical_prediction == 'halts':
        contradiction = "H বলেছিল D হল্ট করবে -- কিন্তু D-এর নির্মাণ অনুযায়ী D আসলে চিরকাল লুপ করে -- H ভুল!"
    else:
        contradiction = "H বলেছিল D লুপ করবে -- কিন্তু D-এর নির্মাণ অনুযায়ী D আসলে হল্ট করে (accept) -- H ভুল!"
    print(f"যদি H(⟨D⟩,⟨D⟩) predict করে '{hypothetical_prediction}':")
    print(f"  D-এর প্রকৃত আচরণ: {actual_behavior}")
    print(f"  ফলাফল: {contradiction}\n")

print("H যা-ই predict করুক -- 'halts' বা 'loops_forever' -- উভয় ক্ষেত্রেই ভুল প্রমাণিত হয়।")
print("তাই এমন কোনো সঠিক, সাধারণ-উদ্দেশ্যে H থাকতে পারে না -- HALT আনডিসাইডেবল। ∎")

    
লক্ষ্য করুন কোড সেলটি predicted_verdict-এর দুটো সম্ভাব্য মান-ই ('halts' এবং 'loops_forever') পরীক্ষা করছে — একটি হাতে-বাছাই করা কেসে সীমাবদ্ধ না থেকে। এটাই প্রমাণটিকে সম্পূর্ণ (exhaustive) করে তোলে — H যা-ই বলুক না কেন, উভয় সম্ভাব্য উত্তরেই D-এর প্রকৃত আচরণ সেই উত্তরকে মিথ্যা প্রমাণ করে, যেমনটা উপরের প্রমাণের ৩ নম্বর ধাপে দেখানো হয়েছে।
মূল কথা · Key takeaway

HALT আনডিসাইডেবল — এটি টুরিং মেশিনের কোনো দুর্বলতা নয়, বরং একটি ফরমালি প্রমাণিত, চিরস্থায়ী গাণিতিক সীমাবদ্ধতা। L38-এর ভাষায় বললে — HALT recognizable (একটি TM $M$-কে $w$-এর উপর সিমুলেট করে, হল্ট করলেই accept করা যায়), কিন্তু $\overline{HALT}$ recognizable নয় — তাই L38-এর ইন্টারলিভড-সিমুলেশন টেকনিকও এখানে কাজ করে না। L40-এ এই একই HALT-কে ভিত্তি ধরে, রিডাকশনের মাধ্যমে আরও অনেক ভাষা আনডিসাইডেবল প্রমাণ করা হবে।

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

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

প্র ০১ কেন আমরা "H আছে ধরে নিয়ে" প্রমাণ শুরু করি, সরাসরি "H নেই" প্রমাণ না করে?

এটি প্রুফ বাই কনট্রাডিকশনের একটি ক্লাসিক প্রয়োগ (L03-এ প্রথম আলোচিত) — "H নেই" এমন একটি নেতিবাচক দাবি সরাসরি প্রমাণ করা কঠিন (আপনাকে সব সম্ভাব্য TM যাচাই করতে হতো, যার সংখ্যা অসীম)। বরং, "H আছে" ধরে নিয়ে, সেই অনুমান থেকে একটি সুনির্দিষ্ট, অবশ্যম্ভাবী লজিক্যাল কনট্রাডিকশন বের করা অনেক সহজ ও নিশ্চিত — যেহেতু অনুমান থেকে একটি অসম্ভব পরিস্থিতি (H একই সাথে সঠিক ও ভুল) বেরিয়ে আসে, তাই মূল অনুমানটাই (H-এর অস্তিত্ব) মিথ্যা হতে বাধ্য।

প্র ০২ D-কে D-এর নিজের বর্ণনার উপর চালানো (self-reference) কেন এত গুরুত্বপূর্ণ?

কারণ এখানেই আসল "ফাঁদ" তৈরি হয় — D সাধারণভাবে অন্য যেকোনো মেশিন $M$-এর জন্য H-এর predict-এর বিপরীত করে, কোনো সমস্যা ছাড়াই। কিন্তু যখন $M = D$ নিজেই হয়, তখন D-এর predict-বিপরীত-করার আচরণটা নিজের সম্পর্কেই প্রযোজ্য হয়ে যায় — D নিজের ভবিষ্যদ্বাণীকে নিজেই ব্যর্থ করে দেয়। এই self-reference ছাড়া কোনো কনট্রাডিকশনই তৈরি হতো না — D শুধু একটি সাধারণ, ঠিকঠাক-কাজ-করা প্রোগ্রাম হয়ে থাকত।

প্র ০৩ এই প্রমাণ Cantor-এর ডায়াগোনাল আর্গুমেন্টের সাথে গঠনগতভাবে কীভাবে সম্পর্কিত?

Cantor-এর আর্গুমেন্টে, একটি তালিকার প্রতিটি সেট থেকে "ডিফার" করে এমন একটি নতুন সেট বানানো হয় (নিজের diagonal এন্ট্রি উল্টে) — প্রমাণ করে সেই তালিকা কখনোই সম্পূর্ণ হতে পারে না। এখানে, D বানানো হয়েছে যাতে সে H-এর predict থেকে সবসময় "ডিফার" করে (ঠিক বিপরীত করে) — প্রমাণ করে H কখনোই সঠিক হতে পারে না। দুটোই একই প্যাটার্ন — "প্রতিটি সম্ভাব্য উত্তর/এন্ট্রি থেকে ইচ্ছাকৃতভাবে ভিন্ন এমন কিছু নির্মাণ করো" — যা যেকোনো প্রস্তাবিত সম্পূর্ণ তালিকা বা সঠিক সমাধানকে অসম্ভব প্রমাণ করে।

অনুশীলন

  1. চিন্তা করুন: যদি কেউ দাবি করে যে সে এমন একটি প্রোগ্রাম বানিয়েছে যা প্রতিটি সম্ভাব্য প্রোগ্রাম ও ইনপুটের জন্য নির্ভুলভাবে বলতে পারে সেটি হল্ট করবে কি না — এই পাঠের প্রমাণ ব্যবহার করে আপনি কীভাবে যুক্তি দিয়ে বলবেন এটা অসম্ভব?

    তাদের দাবিকৃত প্রোগ্রামটাকেই $H$ ধরে নিন। তারপর এই পাঠের ঠিক এই নির্মাণ অনুসরণ করে $D$ বানান (যা তাদের $H$-কে ব্যবহার করে নিজের বিপরীত করে), এবং $D$-কে $D$-এর নিজের বর্ণনার উপর চালান। ঠিক এই পাঠের যুক্তি অনুযায়ী, তাদের $H$ অবশ্যই এই নির্দিষ্ট ইনপুটে ($\langle D, D \rangle$) ভুল উত্তর দেবে — অর্থাৎ তাদের দাবিকৃত "নির্ভুল, সাধারণ-উদ্দেশ্যে" প্রোগ্রামটা আসলে নির্ভুল নয়। এটাই দেখায় কেন এমন কোনো প্রোগ্রাম কখনো সত্যিই থাকতে পারে না।

  2. পরীক্ষা করুন: কোড সেলে diagonal_machine ফাংশনের ভেতরে if/else শাখা দুটো সাময়িকভাবে উল্টে দিন (অর্থাৎ 'halts' হলে D হল্ট করবে, 'loops_forever' হলে D লুপ করবে) এবং চিন্তা করুন — এই পরিবর্তিত সংস্করণেও কি এখনও একটি কনট্রাডিকশন পাওয়া যাবে?

    হ্যাঁ — কনট্রাডিকশনটা এখনও থাকবে, কারণ মূল বিষয়টা হলো D সবসময় H-এর predict-এর বিপরীত করে, নির্দিষ্ট কোন branch কোন কাজ করছে তা গুরুত্বপূর্ণ নয়। শুধু "H যা বলে, D তার উল্টো করে" — এই মূল কাঠামোটাই কনট্রাডিকশন তৈরি করার জন্য যথেষ্ট, দিক পাল্টালেও (উভয় শাখা অদলবদল করলেও) একই যুক্তি প্রযোজ্য থাকবে, শুধু নির্দিষ্ট শব্দগুলো ('halts' বনাম 'loops_forever') অদলবদল হবে।

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

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