P বনাম NP ও অ্যালগরিদম ডিজাইনে এর প্রভাব
এই পাঠে যা শিখবেন
- P ও NP ক্লাসের অনানুষ্ঠানিক কিন্তু সুনির্দিষ্ট সংজ্ঞা
- "সার্টিফিকেট" ও "ভেরিফায়ার" ধারণা — কেন "যাচাই করা সহজ" আর "খুঁজে বের করা সহজ" এক জিনিস নয়
- একটি ছোট বুলিয়ান ফর্মুলার জন্য সত্যিকারের, চলমান পলিনোমিয়াল-টাইম ভেরিফায়ার কোড
- P=?NP প্রশ্নটি এখনও কেন খোলা, এবং এটি নিজের অ্যালগরিদম ডিজাইনের কাজে কীভাবে সিদ্ধান্ত নিতে সাহায্য করে
১ · P — যা দ্রুত সমাধান করা যায়
এই কোর্সের M2-M10 পর্যন্ত যত অ্যালগরিদম দেখেছি — মার্জ সর্ট, ডাইজকস্ট্রা, DP-ভিত্তিক সমাধান — তাদের প্রায় সবগুলোই একটি সাধারণ ক্লাসের অন্তর্ভুক্ত:
$$P = \{\, L : L\text{-কে সিদ্ধান্ত নেওয়ার মতো একটি ডিটারমিনিস্টিক অ্যালগরিদম আছে যা } O(n^k) \text{ সময়ে চলে, কোনো ধ্রুবক } k \text{-এর জন্য} \,\}$$
সহজ কথায়: PPolynomial timeইনপুটের আকার $n$ বাড়লে রানটাইম $n$-এর কোনো নির্দিষ্ট ঘাতে (পাওয়ারে) বাড়ে — $n^2$, $n^3$ ইত্যাদি — $2^n$ বা $n!$-এর মতো নয়। হলো সেই সমস্যাগুলো যেগুলোর জন্য আমরা সরাসরি, কোনো ট্রায়াল-অ্যান্ড-এরর ছাড়াই, একটি কার্যকর সমাধান অ্যালগরিদম লিখতে পারি। L05-L06-এ ইতিমধ্যে এই "পলিনোমিয়াল বনাম এক্সপোনেনশিয়াল" গ্রোথ রেটের পার্থক্য বিস্তারিত দেখা হয়েছে — এখানে আমরা সেটাকে একটি সমস্যার ক্লাস সংজ্ঞায়িত করতে ব্যবহার করছি।
২ · NP — যা দ্রুত যাচাই করা যায়
এখন প্রশ্ন হলো: এমন সমস্যা কি আছে যেগুলোর জন্য সমাধান খুঁজে বের করা কঠিন মনে হয়, কিন্তু কেউ একটা সমাধান প্রস্তাব করলে সেটা সঠিক কিনা যাচাই করা দ্রুত? উত্তর হ্যাঁ — এবং এই ধরনের সমস্যাগুলোর ক্লাসের নামই NP (Nondeterministic Polynomial time)।
$$L \in NP \iff \exists\ \text{পলিনোমিয়াল } p \text{ ও পলিনোমিয়াল-টাইম অ্যালগরিদম } V \text{ এমন যে, প্রতিটি } x \text{-এর জন্য: } x \in L \iff \exists\, c,\ |c| \le p(|x|),\ V(x, c) = \text{Accept}$$
এখানে $c$-কে বলা হয় সার্টিফিকেট (certificate) বা প্রমাণ, আর $V$-কে বলা হয় ভেরিফায়ার (verifier)। লক্ষণীয় — এই সংজ্ঞায় কোথাও বলা নেই যে $c$ কীভাবে খুঁজে পাওয়া যাবে; শুধু বলা হয়েছে, $c$ থাকলে $V$ সেটাকে পলিনোমিয়াল সময়ে চেক করতে পারবে।
একটি বুলিয়ান ফর্মুলা $\phi$ নিন, যেমন $\phi = (x_0 \lor x_1 \lor \lnot x_2) \land (\lnot x_0 \lor x_2) \land (x_1 \lor \lnot x_2) \land (\lnot x_1 \lor \lnot x_0 \lor x_2)$। প্রশ্ন: এমন কোনো $\text{True}/\text{False}$ assignment আছে কিনা যা $\phi$-কে True করে? $n$ ভ্যারিয়েবলের জন্য $2^n$টি সম্ভাব্য assignment আছে — সবগুলো try করা এক্সপোনেনশিয়াল কাজ। কিন্তু কেউ যদি একটা candidate assignment $c$ প্রস্তাব করে, সেটা $\phi$-কে সন্তুষ্ট করে কিনা যাচাই করতে শুধু প্রতিটি ক্লজে একবার করে চোখ বুলিয়ে দেখতে হয় — এটি $\phi$-এর আকারের সাপেক্ষে লিনিয়ার (তাই পলিনোমিয়াল) কাজ।
নিচের কোডে ঠিক এই ভেরিফায়ারটাই বাস্তবে লেখা হয়েছে — উপরের $\phi$ ফর্মুলাটি ক্লজের একটি লিস্ট হিসেবে
প্রতিনিধিত্ব করা হয়েছে, এবং verify_assignment ফাংশনটি একটি candidate assignment নিয়ে সেটা
$\phi$-কে সন্তুষ্ট করে কিনা চেক করে — সমাধান খোঁজে না, শুধু যাচাই করে।
# phi = (x0 OR x1 OR NOT x2) AND (NOT x0 OR x2) AND (x1 OR NOT x2) AND (NOT x1 OR NOT x0 OR x2)
# প্রতিটি ক্লজ = লিটারেলের একটি লিস্ট; প্রতিটি লিটারেল = (variable_index, negated_কিনা)
formula = [
[(0, False), (1, False), (2, True)],
[(0, True), (2, False)],
[(1, False), (2, True)],
[(1, True), (0, True), (2, False)],
]
def verify_assignment(formula, assignment):
"""সার্টিফিকেট (candidate assignment) যাচাই করে -- প্রতিটি ক্লজে
অন্তত একটি লিটারেল True কিনা দেখে। সময় জটিলতা O(m), যেখানে m হলো
ফর্মুলার মোট লিটারেল সংখ্যা -- ইনপুটের (n ভ্যারিয়েবল) সাপেক্ষে পলিনোমিয়াল।"""
literal_checks = 0
for clause in formula:
clause_satisfied = False
for var_idx, negated in clause:
literal_checks += 1
value = assignment[var_idx]
if negated:
value = not value
if value:
clause_satisfied = True
break
if not clause_satisfied:
return False, literal_checks
return True, literal_checks
candidate = [True, True, True] # x0=True, x1=True, x2=True -- এটাই "সার্টিফিকেট"
is_sat, checks = verify_assignment(formula, candidate)
print(f"candidate {candidate} -> সন্তুষ্ট: {is_sat}, মোট লিটারেল-চেক: {checks}")
wrong_candidate = [True, True, False]
is_sat2, checks2 = verify_assignment(formula, wrong_candidate)
print(f"candidate {wrong_candidate} -> সন্তুষ্ট: {is_sat2}, মোট লিটারেল-চেক: {checks2}")
n = 3
print(f"\n{n} ভ্যারিয়েবলের জন্য সম্ভাব্য assignment সংখ্যা: 2^{n} = {2 ** n}")
print("প্রতিটি candidate যাচাই করতে মাত্র O(m) সময় লাগে -- কিন্তু 'কোনটা কাজ করে' তা")
print("খুঁজে বের করতে ব্রুট-ফোর্সে সবগুলো সম্ভাবনা try করা লাগতে পারে (n বড় হলে এক্সপোনেনশিয়াল)।")
[True, True, True]-এর জন্য verify_assignment সবগুলো ক্লজ চেক করে
True রিটার্ন করে (৭টি লিটারেল-চেকে)। দ্বিতীয় [True, True, False]-এর ক্ষেত্রে
দ্বিতীয় ক্লজ ($\lnot x_0 \lor x_2$) কোনো লিটারেলেই সন্তুষ্ট হয় না, তাই ফাংশনটি তাড়াতাড়ি
False রিটার্ন করে থামে (মাত্র ৩টি চেকে) — বাকি ক্লজগুলো আর দেখারই প্রয়োজন হয় না। দুই ক্ষেত্রেই
কাজের পরিমাণ ফর্মুলার আকারের সাপেক্ষে ছোট ও পলিনোমিয়াল — n ভ্যারিয়েবল হলেও, ভেরিফিকেশনের খরচ কখনোই
$2^n$ স্পর্শ করে না।
৩ · P=?NP — কেন এই প্রশ্নটা এখনো খোলা
এতক্ষণে একটা জিনিস স্পষ্ট হওয়ার কথা: $P \subseteq NP$ — কারণ যদি কোনো সমস্যা পলিনোমিয়াল সময়ে সরাসরি সমাধান করা যায়, তাহলে সেই সমাধান অ্যালগরিদমটাকেই একটা (সার্টিফিকেট উপেক্ষা করা) ভেরিফায়ার হিসেবে ব্যবহার করা যায়। কিন্তু উল্টো দিকটা — $NP \subseteq P$ কিনা, অর্থাৎ যা কিছু দ্রুত যাচাই করা যায় তা কি সবসময় দ্রুত খুঁজেও বের করা যায় — এটাই কম্পিউটার সায়েন্সের সবচেয়ে বিখ্যাত খোলা প্রশ্ন, যাকে বলা হয় $P \overset{?}{=} NP$।
এই প্রশ্নের আনুষ্ঠানিক সংজ্ঞা, ইতিহাস, এবং কেন এটি প্রমাণ করা এত কঠিন — তা নিয়ে Theory of Computation কোর্সের "The P vs NP Question" পাঠ-এ পূর্ণাঙ্গ আলোচনা আছে। P ও NP-এর সম্পূর্ণ আনুষ্ঠানিক সংজ্ঞা ও প্রমাণ পদ্ধতির জন্য দেখুন "Time Complexity and the Class P" এবং "The Class NP and Verifiers" পাঠদুটো।
আপনি কোনো প্রমাণিত উপপাদ্য ছাড়াই প্রতিদিনের কাজে এই সিদ্ধান্তটা নিতে পারবেন: যদি কোনো সমস্যায় প্রস্তাবিত সমাধান যাচাই করা সহজ মনে হয় (অর্থাৎ এটি NP-তে আছে বলে মনে হয়), কিন্তু আপনি সরাসরি একটা পলিনোমিয়াল-টাইম সমাধান অ্যালগরিদম বহুদিন চেষ্টা করেও খুঁজে পাননি — সেটা এই ইঙ্গিত দেয় যে সমস্যাটি হয়তো NP-হার্ড, অর্থাৎ $P = NP$ প্রমাণিত না হওয়া পর্যন্ত এর জন্য কোনো পলিনোমিয়াল-টাইম সঠিক অ্যালগরিদম পাওয়ার সম্ভাবনা কম। L48-এ আমরা ঠিক এই ইঙ্গিতগুলো চেনার একটি প্র্যাকটিক্যাল চেকলিস্ট দেখব।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ যদি একটি সমস্যার জন্য পলিনোমিয়াল-টাইম ভেরিফায়ার থাকে, তাহলে কি সমস্যাটি অবশ্যই P-তেও থাকতে হবে?
না — অন্তত এখনো প্রমাণিত নয়। "পলিনোমিয়াল-টাইম ভেরিফায়ার আছে" মানেই "পলিনোমিয়াল-টাইম সলভার আছে" নয়; এই দুটো বিবৃতি সমতুল্য হবে কেবল যদি $P = NP$ প্রমাণিত হয়। বর্তমানে জানা আছে $P \subseteq NP$ (সমাধান করতে পারলে যাচাইও করা যায়), কিন্তু উল্টোটা এখনো খোলা প্রশ্ন — তাই সাধারণভাবে বলা যায় না যে NP-এর প্রতিটি সমস্যাই P-তে আছে।
প্র ০২
কোড সেলে candidate = [True, True, True] সফল হলো — কিন্তু বাস্তবে এই assignment-টা
আমরা কীভাবে পেলাম?
এখানে মাত্র ৩টি ভ্যারিয়েবল থাকায় হাতে-কলমে ৮টি সম্ভাব্য combination চেক করে বের করা সম্ভব হয়েছে — এটাই
brute force। কিন্তু বাস্তব-জীবনের SAT ইনস্ট্যান্সে শত-হাজার ভ্যারিয়েবল থাকতে পারে, তখন $2^n$ combination
brute force করে খোঁজা ব্যবহারিকভাবে অসম্ভব। লক্ষণীয়, verify_assignment ফাংশনটির খরচ কিন্তু
$n$ যাই হোক না কেন ফর্মুলার আকারের সাপেক্ষে পলিনোমিয়ালই থাকে — এটাই "যাচাই সহজ, খোঁজা কঠিন হতে পারে"
পার্থক্যের মূল কথা।
প্র ০৩
verify_assignment কি সবসময় সবগুলো ক্লজ শেষ পর্যন্ত চেক করে, নাকি মাঝপথে থামতে পারে?
এটি দুই জায়গায় তাড়াতাড়ি থামতে পারে: (১) একটি ক্লজের মধ্যে যেই মুহূর্তে কোনো একটি লিটারেল True হয়ে যায়,
break দিয়ে ওই ক্লজের বাকি লিটারেল আর চেক করে না; (২) যেই মুহূর্তে কোনো একটি ক্লজ সন্তুষ্ট হয়
না, পুরো ফাংশনই সাথে সাথে False রিটার্ন করে, বাকি ক্লজগুলো আর দেখেই না। এই জন্যই কোড সেলের
আউটপুটে ভুল candidate-টির জন্য চেক সংখ্যা (৩) সঠিক candidate-টির (৭) চেয়ে কম — তবে দুই ক্ষেত্রেই worst-case
খরচ ফর্মুলার মোট লিটারেল সংখ্যা $m$-এর বেশি হতে পারে না, তাই এটি এখনও $O(m)$।
অনুশীলন
-
চিন্তা করুন: কোড সেলের
formula-এর জন্যcandidate = [False, True, True](অর্থাৎ $x_0=\text{False}, x_1=\text{True}, x_2=\text{True}$) হাতে-কলমে চেক করে বলুন এটি $\phi$-কে সন্তুষ্ট করে কিনা।হ্যাঁ, সন্তুষ্ট করে। ক্লজ ১: $x_0 \lor x_1 \lor \lnot x_2 = F \lor T \lor F = T$। ক্লজ ২: $\lnot x_0 \lor x_2 = T \lor T = T$। ক্লজ ৩: $x_1 \lor \lnot x_2 = T \lor F = T$। ক্লজ ৪: $\lnot x_1 \lor \lnot x_0 \lor x_2 = F \lor T \lor T = T$। সবগুলো ক্লজ True, তাই পুরো ফর্মুলা সন্তুষ্ট।
-
পরীক্ষা করুন: কোড সেলে
candidate-এর মান[False, True, True]-এ বদলে Run চেপে আপনার হাতে-কলমের হিসাব যাচাই করুন, তারপর[True, False, True]দিয়ে চেষ্টা করে দেখুন এটি কেন ব্যর্থ হয়।[False, True, True]সন্তুষ্ট করবে (আউটপুটেTrueদেখাবে), অনুশীলন ১-এর হিসাবের সাথে মিলে যাবে।[True, False, True]-এর ক্ষেত্রে ক্লজ ৩ ($x_1 \lor \lnot x_2$) ব্যর্থ হবে ($x_1=\text{False}$, $\lnot x_2 = \lnot\text{True} = \text{False}$, তাই $F \lor F = F$) — ফাংশনটি সেখানেই থেমেFalseরিটার্ন করবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- Theory of Computation: "The Class NP and Verifiers" পূর্ণ আনুষ্ঠানিক সংজ্ঞা NP-এর সম্পূর্ণ আনুষ্ঠানিক সংজ্ঞা, ভেরিফায়ার-ভিত্তিক ও নন-ডিটারমিনিস্টিক টুরিং মেশিন-ভিত্তিক — দুই দৃষ্টিকোণ থেকেই।
- Theory of Computation: "The P vs NP Question" খোলা প্রশ্নের ইতিহাস P=?NP প্রশ্নের সম্পূর্ণ প্রেক্ষাপট, এর তাৎপর্য, এবং কেন এটি প্রমাণ করা এত কঠিন।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ M11-এর পরের দুটি পাঠে NP-হার্ড সমস্যা চেনার প্র্যাকটিক্যাল চেকলিস্ট এবং তার মুখোমুখি হলে কী করণীয় তা দেখব।