সরাসরি পর্বে যান
পর্ব ৬৪ · ৭২-এর মধ্যে · মডিউল ৮

P বনাম NP — NP-complete আর reduction

P vs NP — Decision Problems, Certificates, Polynomial-Time Reductions, NP-Hardness, Cook–Levin & Karp’s Reduction Chain

Taught in Bangla, this lesson explains what efficient means and how P differs from NP as solving versus verifying with certificates. It teaches polynomial-time reductions, NP-hardness and NP-completeness, Cook–Levin and Karp's reductions, and works through 3-SAT to independent set, vertex cover and clique, Hamiltonian cycle to TSP and the pseudo-polynomial subset sum trap, then discusses co-NP and recognising NP-hard problems in interviews and contests.

এই পর্বে যা শিখবেন

  • Decision আর optimization problem-এর সম্পর্ক, আর কেন polynomial সময়কে “দক্ষ” ধরা হয় (Cobham–Edmonds thesis)
  • P, NP আর co-NP-এর সংজ্ঞা — certificate আর verifier দিয়ে; আর “NP = non-polynomial” ভুল ধারণাটি কেন ভুল
  • Polynomial-time reduction কী, কোন দিকে করতে হয়, আর NP-hard ও NP-complete প্রমাণের চার ধাপ
  • 3-SAT → independent set, independent set ↔ vertex cover ↔ clique আর Hamiltonian cycle → TSP — হাতে trace আর কোডে যাচাই
  • Pseudo-polynomial আর polynomial-এর পার্থক্য, NP-intermediate, আর ইন্টারভিউ-কনটেস্টে NP-hard সমস্যা চেনার কৌশল

১ · সহজ সমস্যা, কঠিন সমস্যা — আর “দক্ষ” মানে কী

এই কোর্সে এ পর্যন্ত প্রায় সব সমস্যার একটি দ্রুত অ্যালগরিদম পেয়েছেন: sort-এ O(n log n), shortest path-এ O((V + E) log V), MST-তে O(E log V). কিন্তু পর্ব ৫৬ আর ৫৭-এ এমন কিছু সমস্যা দেখেছেন — Hamiltonian cycle, traveling salesman, graph colouring — যেখানে backtracking আর branch and bound-এর বেশি কিছু পাওয়া যায়নি, আর worst case-এ সময় 2ⁿ বা n!-এর ধাঁচে। প্রশ্নটি স্বাভাবিক: এগুলোর দ্রুত অ্যালগরিদম কি আমরা এখনো খুঁজে পাইনি, নাকি এগুলো সত্যিই কঠিন? এই পর্বের পুরো তত্ত্ব এই প্রশ্নটি সঠিকভাবে জিজ্ঞেস করার ভাষা।

Decision আর optimization

তত্ত্বের সুবিধার জন্য সমস্যাগুলোকে হ্যাঁ/না প্রশ্নে লেখা হয় — decision problem. “সবচেয়ে ছোট vertex cover কত বড়?” একটি optimization problem; তার decision রূপ: “k বা তার কম vertex দিয়ে কি cover করা যায়?” এতে কিছু হারায় না। Decision রূপের দ্রুত সমাধান থাকলে k = 0, 1, 2, … (বা পর্ব ১৯-এর মতো k-এর ওপর binary search) চেষ্টা করে সর্বনিম্ন মানটি পাওয়া যায় — মাত্র O(log n) বার ডাকলেই। উল্টো দিকে, optimization সমাধান করতে পারলে decision তো সাথে সাথেই। তাই কঠিনতার প্রশ্নে দুটি প্রায় সমান, আর decision রূপ নিয়ে কথা বলা অনেক পরিষ্কার।

Polynomial সময় কেন “দক্ষ”

একটি অ্যালগরিদম polynomial time-এর, যদি ইনপুটের আকার n হলে সময় O(nᶜ) হয় কোনো স্থির c-এর জন্য — n, n log n, n², n³ সবই এর মধ্যে। 2ⁿ, n!, nⁿ এর বাইরে। পার্থক্যটি সংখ্যায় দেখুন, ধরে নিয়ে কম্পিউটার সেকেন্ডে ১০৮টি কাজ করে:

nn²n³2ⁿn!
২০৪০০ — মুহূর্তে৮,০০০ — মুহূর্তেপ্রায় ১০ লাখ — ০.০১ সেকেন্ডপ্রায় ২.৪ × ১০১৮ — ৭০০ বছরের বেশি
৫০২,৫০০ — মুহূর্তে১,২৫,০০০ — মুহূর্তেপ্রায় ১.১ × ১০১৫ — প্রায় ১৩০ দিনঅকল্পনীয়
১০০১০,০০০ — মুহূর্তে১০ লাখ — ০.০১ সেকেন্ডপ্রায় ১.৩ × ১০৩০ — মহাবিশ্বের বয়সের বহু গুণঅকল্পনীয়

আরও গুরুত্বপূর্ণ একটি পার্থক্য: কম্পিউটার দ্বিগুণ দ্রুত হলে n² অ্যালগরিদম একই সময়ে প্রায় ১.৪১ গুণ বড় ইনপুট সামলায়, কিন্তু 2ⁿ অ্যালগরিদম মাত্র একটি বেশি — n থেকে n + 1. Exponential অ্যালগরিদমকে হার্ডওয়্যার দিয়ে বাঁচানো যায় না।

১৯৬৫ সালে দুজন গবেষক আলাদাভাবে প্রস্তাব করেন যে “দক্ষ অ্যালগরিদম” মানে polynomial সময়ের অ্যালগরিদম — Alan Cobham (“The intrinsic computational difficulty of functions”) আর Jack Edmonds (“Paths, trees, and flowers”, যেখানে তিনি সাধারণ গ্রাফে maximum matching-এর polynomial অ্যালগরিদম দেন)। একে বলা হয় Cobham–Edmonds thesis. এটি উপপাদ্য নয়, একটি কাজের সংজ্ঞা — n¹⁰⁰ বাস্তবে মোটেই দ্রুত নয়। তবু সংজ্ঞাটি টিকে আছে তিনটি কারণে: বাস্তবে যে polynomial অ্যালগরিদম পাওয়া যায় তাদের ঘাত প্রায় সবসময় ছোট (২ বা ৩); polynomial-এর শ্রেণি যোগ, গুণ আর একটির ভেতর আরেকটি ডাকায় বন্ধ থাকে (polynomial-এর polynomial আবার polynomial); আর যুক্তিসংগত সব কম্পিউটার-মডেলে (RAM, Turing machine) একই সমস্যাগুলো polynomial থাকে।

২ · P আর NP — সমাধান করা বনাম যাচাই করা

উপমা — পত্রিকার sudoku আর বিয়ের বসার তালিকা। খবরের কাগজের একটি কঠিন sudoku মেলাতে আপনার হয়তো এক ঘণ্টা লাগে। কিন্তু পাশের বাসার কেউ ভরা ধাঁধাটি দেখালে আপনি দুই মিনিটে বলে দিতে পারবেন সেটি ঠিক কি না — প্রতিটি সারি, কলাম আর বাক্সে ১ থেকে ৯ একবার করে আছে কি না দেখলেই হলো। আরেকটি: বিয়ের অনুষ্ঠানে ২০০ জন অতিথি, ২০টি টেবিল, আর কিছু আত্মীয়ের মধ্যে পুরোনো মনোমালিন্য — তাদের এক টেবিলে বসানো যাবে না। এমন একটি বসার তালিকা বানানো মাথা খারাপ করা কাজ। কিন্তু কেউ একটি তালিকা হাতে দিলে, মনোমালিন্যের প্রতিটি জোড়া মিলিয়ে দেখে তালিকাটি ঠিক কি না বলা সহজ।

এই “খুঁজে বের করা কঠিন, কিন্তু দেখে মিলিয়ে নেওয়া সহজ” — এটিই NP-এর মূল কথা। সংজ্ঞাগুলো:

Class P

P = সেইসব decision problem, যাদের একটি polynomial সময়ের অ্যালগরিদম আছে। উদাহরণ: অ্যারে সাজানো আছে কি না, গ্রাফ connected কি না, s থেকে t-তে k-এর কম দৈর্ঘ্যের পথ আছে কি না, 2-SAT (পর্ব ৪৬), কোনো সংখ্যা মৌলিক কি না (২০০২ সালে AKS অ্যালগরিদম)।

Class NP

NP = সেইসব decision problem, যাদের প্রতিটি “হ্যাঁ” ইনপুটের জন্য একটি ছোট (ইনপুটের polynomial আকারের) প্রমাণ আছে, আর একটি polynomial সময়ের verifier সেই প্রমাণ মিলিয়ে “হ্যাঁ” নিশ্চিত করতে পারে। “না” ইনপুটে কোনো প্রমাণই verifier-কে ঠকাতে পারে না।

এই প্রমাণকে বলে certificate (বা witness, সাক্ষী)। Vertex cover-এ certificate হলো vertex-গুলোর তালিকা; SAT-এ চলকগুলোর মান; Hamiltonian cycle-এ vertex-এর ক্রম; subset sum-এ বাছাই করা সংখ্যাগুলো; sudoku-তে ভরা বোর্ড; বিয়ের সমস্যায় (আসলে graph colouring — অতিথি vertex, মনোমালিন্য edge, টেবিল রং) প্রত্যেকের টেবিল নম্বর। প্রতিটি ক্ষেত্রে certificate ছোট, আর মিলিয়ে দেখতে polynomial সময় লাগে।

নিচের গ্রাফে প্রশ্ন: ৩টি বা কম vertex দিয়ে কি সব edge cover করা যায়? Verifier কীভাবে দুটি certificate যাচাই করে দেখুন:

গ্রাফ G (৫টি vertex, ৬টি edge)সবুজ = certificate-এর vertex

            
প্রতিটি ধাপের পর
certificate S
এই ধাপে দেখা edge
রায়
  1. আকার যাচাই. Certificate S = {1, 2, 4}, k = 3. আকার ঠিক আছে; এখন একটি boolean অ্যারেতে S-এর vertex চিহ্নিত — O(V).
  2. Edge 0–1, 0–2. 0 সেটে নেই, কিন্তু 1 আর 2 আছে — দুটিই cover হয়েছে।
  3. Edge 1–2, 1–3. দুটিতেই 1 আছে।
  4. Edge 2–4, 3–4 — রায়. শেষ দুটিতে 4. ছয়টি edge-এর প্রতিটি একবার দেখা হলো, প্রতিবার O(1): মোট O(V + E). Verifier “হ্যাঁ” বলে।
  5. দ্বিতীয় certificate. কেউ দাবি করলেন S = {0, 3, 4}-ও চলবে। আকার ঠিক আছে, চিহ্নিত করা হলো।
  6. ধরা পড়ল. 0–1 আর 0–2-এ 0 আছে, কিন্তু 1–2-এর কোনো প্রান্ত সেটে নেই। Verifier সাথে সাথে “না” বলে — একটি খোলা edge-ই যথেষ্ট প্রমাণ। ভুল certificate verifier-কে ঠকাতে পারে না।
  7. Certificate না থাকলে. নিজে খুঁজতে হলে সব subset চেষ্টা করার সরল পদ্ধতিতে 2⁵ = ৩২টি; n = 100 হলে 2¹⁰⁰. যাচাই O(V + E), খোঁজা exponential — এই ব্যবধানই P বনাম NP প্রশ্নের হৃদয়।

নিচের প্রোগ্রাম একই কথা সংখ্যায় দেখায়। একটি ring গ্রাফে (0–1–2–…–(n−1)–0, এখানে n জোড়) সবচেয়ে ছোট vertex cover-এর আকার n/2 — একটি করে বাদ দিয়ে vertex নিলেই হয়। Verifier certificate মেলায় O(V + E)-এ; brute-force search সব 2ⁿ subset চেষ্টা করে গোনে কতবার চেষ্টা লাগল:

#include <bit>
#include <initializer_list>
#include <iostream>
#include <utility>
#include <vector>
using namespace std;
using Edges = vector<pair<int, int>>;

Edges ring(int n) {                              // cycle 0-1-2-...-(n-1)-0
    Edges e;
    for (int i = 0; i < n; ++i) e.push_back({i, (i + 1) % n});
    return e;
}
// VERIFIER, O(V + E): is S a vertex cover with |S| <= k?
bool verify(int n, const Edges& e, const vector<int>& S, int k) {
    if ((int)S.size() > k) return false;
    vector<bool> in(n, false);
    for (int v : S) in[v] = true;
    for (auto [u, v] : e) if (!in[u] && !in[v]) return false;   // an uncovered edge
    return true;
}
// SEARCH, O(2^n * E): try every subset as a bitmask, count the tries
bool search(int n, const Edges& e, int k, long long& tries) {
    tries = 0;
    for (unsigned long long m = 0; m < (1ULL << n); ++m) {
        ++tries;
        if (popcount(m) > k) continue;
        bool ok = true;
        for (auto [u, v] : e) if (!(m >> u & 1) && !(m >> v & 1)) { ok = false; break; }
        if (ok) return true;
    }
    return false;
}
int main() {
    Edges e = ring(10);
    cout << "verify {0 2 4 6 8}, k=5: " << (verify(10, e, {0, 2, 4, 6, 8}, 5) ? "YES" : "NO") << '\n';
    cout << "verify {0 2 4 6 7}, k=5: " << (verify(10, e, {0, 2, 4, 6, 7}, 5) ? "YES" : "NO") << '\n';
    for (int n : {8, 12, 16, 20})
        for (int k : {n / 2 - 1, n / 2}) {
            long long tries;
            bool found = search(n, ring(n), k, tries);
            cout << "ring n=" << n << " k=" << k << ": " << (found ? "yes" : "no ") << "  subsets tried=" << tries << '\n';
        }
}

আউটপুট:

verify {0 2 4 6 8}, k=5: YES
verify {0 2 4 6 7}, k=5: NO
ring n=8 k=3: no   subsets tried=256
ring n=8 k=4: yes  subsets tried=86
ring n=12 k=5: no   subsets tried=4096
ring n=12 k=6: yes  subsets tried=1366
ring n=16 k=7: no   subsets tried=65536
ring n=16 k=8: yes  subsets tried=21846
ring n=20 k=9: no   subsets tried=1048576
ring n=20 k=10: yes  subsets tried=349526

প্রথম দুই লাইন verifier: {0 2 4 6 8} সব edge ছোঁয়, কিন্তু {0 2 4 6 7}-এ edge 8–9-এর কোনো প্রান্ত নেই — ১০টি edge দেখেই রায়। পরের লাইনগুলো search. k = n/2 − 1-এ উত্তর “না”, আর “না” নিশ্চিত হতে সব 2ⁿ subset দেখতে হয়েছে: 256, 4096, 65536, 1048576 — n চার বাড়লে চেষ্টা ১৬ গুণ। k = n/2-এ উত্তর “হ্যাঁ” আর আগে থামে (প্রথম পাওয়া cover হলো জোড় vertex-গুলো, mask 0101…01), কিন্তু তবুও চেষ্টার সংখ্যা প্রায় 2ⁿ/3 — exponential. popcount (C++20, <bit>) mask-এর 1-bit গোনে। Search-এর সময় O(2ⁿ · E), জায়গা O(V + E); verifier-এর সময় O(V + E). লক্ষ করুন, “না” উত্তরের জন্য এখানে কোনো ছোট certificate নেই — সেটি অংশ ৮-এর co-NP প্রশ্ন।

NP মানে “non-polynomial” নয়

সবচেয়ে প্রচলিত ভুল: NP-এর পুরো নাম Nondeterministic Polynomial time — “non-polynomial” নয়। মূল সংজ্ঞাটি একটি কাল্পনিক যন্ত্রের: nondeterministic Turing machine, যা প্রতিটি মোড়ে “ঠিক পথটি অনুমান করে” নিতে পারে; NP হলো এমন যন্ত্রে polynomial সময়ে সমাধানযোগ্য সমস্যা। “ঠিক অনুমান” আর “certificate” একই জিনিস — অনুমান করা পথটিই প্রমাণ। আর P-এর প্রতিটি সমস্যা NP-তেও আছে: verifier certificate উপেক্ষা করে নিজেই polynomial সময়ে সমাধান করে ফেলে। তাই P ⊆ NP; সাজানো, shortest path, MST — সবই NP-তে। “এই সমস্যা NP, তাই কঠিন” কথাটি ভুল; সঠিক কথা “এটি NP-hard বা NP-complete”.

৩ · Reduction — একটি সমস্যাকে আরেকটির ভাষায় লেখা

উপমা — ডিজিটাল ওজনের যন্ত্র। বাজারের দোকানির কাছে একটি ভালো ডিজিটাল দাঁড়িপাল্লা আছে। আপনার প্রশ্ন: “এই ব্যাগের চাল কি ৫ কেজির বেশি?” আপনি নিজে নিয়ম বানাতে বসেন না — ব্যাগটি পাল্লায় তুলে দেন। যদি প্রশ্নটি সবসময় এভাবে “পাল্লার ভাষায়” বদলে নেওয়া যায়, তবে আপনার প্রশ্ন পাল্লার কাজের চেয়ে কঠিন হতে পারে না। কেউ যদি প্রমাণ করেন যে আপনার প্রশ্ন আসলে খুব কঠিন, তাহলে বুঝতে হবে পাল্লাটিও অন্তত ততটাই কঠিন কাজ করছে।

সমস্যা A থেকে B-তে একটি polynomial-time reduction, লেখা হয় A ≤p B, হলো একটি polynomial সময়ের রূপান্তর f, যা A-এর প্রতিটি ইনপুট x-কে B-এর একটি ইনপুট f(x)-এ বদলায়, এমনভাবে যে

x A-এর “হ্যাঁ” ইনপুট  ⇔  f(x) B-এর “হ্যাঁ” ইনপুট

এর মানে: B-এর একটি দ্রুত অ্যালগরিদম থাকলে A-এরও আছে — x নিন, f(x) বানান, B-এর অ্যালগরিদম চালান। Polynomial-এর ভেতর polynomial আবার polynomial, তাই মোট সময়ও polynomial. উল্টো দিক থেকে পড়লে আরও কাজের: A কঠিন হলে B-ও অন্তত ততটা কঠিন। চিহ্ন “≤” এই কারণেই — A-এর কঠিনতা B-এর কঠিনতার চেয়ে বেশি নয়। Reduction transitive: A ≤p B আর B ≤p C হলে A ≤p C (দুটি রূপান্তর পরপর চালান)।

দিক উল্টাবেন না। নতুন সমস্যা X কঠিন প্রমাণ করতে চাইলে একটি জানা কঠিন সমস্যা থেকে X-এ reduce করুন: জানা ≤p X. “আমি X-কে SAT-এ বদলে ফেলেছি, তাই X NP-hard” — ভুল; এতে শুধু বোঝা যায় X SAT-এর চেয়ে কঠিন নয়, যা প্রায় সব সমস্যার জন্যই সত্য (“অ্যারেটি সাজানো কি না”-র মতো সহজ প্রশ্নও SAT-এ বদলানো যায়)। আর ⇔-এর দুই দিকই প্রমাণ করতে হয়: “হ্যাঁ থেকে হ্যাঁ” আর “f(x) হ্যাঁ হলে x-ও হ্যাঁ”.

NP-hard আর NP-complete

NP-hard

সমস্যা B NP-hard, যদি NP-এর প্রতিটি সমস্যা A-এর জন্য A ≤p B. B নিজে NP-তে থাকতেই হবে এমন নয় — optimization রূপ (“সবচেয়ে ছোট cover কত?”), এমনকি halting problem-এর মতো সমাধান-অযোগ্য সমস্যাও NP-hard.

NP-complete

B NP-complete, যদি (১) B ∈ NP আর (২) B NP-hard. এরা NP-এর “সবচেয়ে কঠিন” সমস্যা: এদের যেকোনো একটির polynomial অ্যালগরিদম পেলে P = NP; আর P ≠ NP হলে এদের কোনোটিরই polynomial অ্যালগরিদম নেই।

প্রতিটি নতুন সমস্যার জন্য NP-এর সব সমস্যা থেকে reduction দেখানো অসম্ভব। কিন্তু transitivity বাঁচিয়ে দেয়: একটি জানা NP-complete সমস্যা C থেকে C ≤p X দেখালেই সব NP সমস্যা C হয়ে X-এ পৌঁছায়। তাই NP-completeness প্রমাণের চারটি ধাপ:

  1. X ∈ NP: certificate কী, আর verifier কেন polynomial — বলুন।
  2. একটি জানা NP-complete সমস্যা C বাছুন — 3-SAT, vertex cover, Hamiltonian cycle, subset sum ইত্যাদি, যেটি X-এর গঠনের সবচেয়ে কাছে।
  3. রূপান্তর f বানান C-এর ইনপুট থেকে X-এর ইনপুটে, আর দেখান f polynomial সময়ে চলে।
  4. সঠিকতা প্রমাণ করুন দুই দিকেই: C-এর হ্যাঁ ⇒ X-এর হ্যাঁ, আর X-এর হ্যাঁ ⇒ C-এর হ্যাঁ.

৪ · প্রথম NP-complete সমস্যা — Cook–Levin আর Karp-এর ২১টি

শিকলের প্রথম আংটা কোথা থেকে? কাউকে সরাসরি দেখাতে হয়েছিল যে NP-এর প্রতিটি সমস্যা একটি নির্দিষ্ট সমস্যায় reduce হয়। সেই সমস্যা SAT: কয়েকটি true/false চলকের একটি যুক্তি-সূত্র — চলকগুলোর এমন মান আছে কি, যাতে সূত্রটি true হয়? সাধারণত সূত্রটি থাকে CNF রূপে: কয়েকটি clause-এর AND, প্রতিটি clause কয়েকটি literal-এর OR. যেমন

φ = (x₁ ∨ x₂ ∨ x₃) ∧ (¬x₁ ∨ ¬x₂ ∨ x₃) ∧ (x₁ ∨ ¬x₂ ∨ ¬x₃)

প্রতিটি clause-এ ঠিক তিনটি literal থাকলে সমস্যাটির নাম 3-SAT. φ satisfiable: x₁ = 1, x₂ = 0, x₃ = 0 দিলে প্রথম clause-এ x₁, দ্বিতীয়তে ¬x₂, তৃতীয়তে x₁ সত্য। এই মানগুলোই certificate — m টি clause-এর প্রতিটি একবার দেখে O(m)-এ যাচাই।

Cook–Levin উপপাদ্য (শুধু ধারণা)

SAT NP-complete. ধারণাটি: NP-এর যেকোনো সমস্যার একটি polynomial verifier আছে — একটি প্রোগ্রাম, যা ইনপুট আর certificate নিয়ে p(n) ধাপে “হ্যাঁ” বা “না” বলে। সেই প্রোগ্রামের পুরো চলা — প্রতিটি ধাপে মেমরির প্রতিটি ঘরে কী আছে — একটি p(n) × p(n) টেবিল। “টেবিলের প্রথম সারি সঠিক শুরু”, “প্রতিটি সারি আগের সারি থেকে যন্ত্রের নিয়ম মেনে আসে”, “শেষে উত্তর হ্যাঁ” — এই সব শর্তকে boolean চলক আর clause দিয়ে লেখা যায়, আর মোট clause polynomial সংখ্যক। সূত্রটি satisfiable ⇔ কোনো certificate আছে যা verifier মেনে নেয়। Cook–Levin theorem তাই বলে: “যাচাই করা যায়” এমন যেকোনো প্রশ্নকে একটি যুক্তি-সূত্রের প্রশ্নে বদলানো যায়।

ইতিহাস। Stephen Cook ১৯৭১ সালে “The complexity of theorem-proving procedures” প্রবন্ধে (STOC সম্মেলনে) SAT-এর NP-completeness প্রমাণ করেন। সোভিয়েত ইউনিয়নে Leonid Levin প্রায় একই সময়ে স্বাধীনভাবে একই ধারণায় পৌঁছান — “search problem”-এর ভাষায় — আর ১৯৭৩ সালে প্রকাশ করেন; শীতল যুদ্ধের দেয়ালে দুই দিকের কাজ বহু বছর একে অন্যের অজানা ছিল। ১৯৭২ সালে Richard Karp “Reducibility among combinatorial problems” প্রবন্ধে দেখান যে আরও ২১টি পরিচিত সমস্যা NP-complete — clique, vertex cover, set cover, Hamiltonian circuit (directed ও undirected), chromatic number (graph colouring), exact cover, 3-dimensional matching, knapsack, partition, max cut, 0-1 integer programming, আর প্রতিটি clause-এ সর্বোচ্চ তিনটি literal-এর SAT সহ। এই প্রবন্ধই দেখায় যে NP-completeness একটি বিচ্ছিন্ন কৌতূহল নয় — বাস্তবের অসংখ্য সমস্যা একই সুতোয় বাঁধা। Cook ১৯৮২ সালে আর Karp ১৯৮৫ সালে Turing Award পান। ১৯৭৯ সালে Michael Garey আর David Johnson-এর Computers and Intractability বইটি শত শত NP-complete সমস্যার তালিকা দেয়, যা আজও গবেষকদের হাতের বই।

এই পর্বের বাকি অংশে আমরা একটি ছোট শিকল নিজে হাতে বানাব:

SAT ──(Cook–Levin)──► 3-SAT ──► Independent Set ◄──► Vertex Cover
                                       │
                                       ▼
                                    Clique

Hamiltonian cycle ──► TSP (decision)          Subset Sum ──► Knapsack

৫ · 3-SAT → Independent Set — clause-ত্রিভুজ আর বিরোধের edge

গ্রাফের একটি independent set হলো এমন vertex-এর সেট, যাদের কোনো দুজনের মধ্যে edge নেই — বন্ধুসভায় এমন কয়েকজনকে ডাকা, যাদের কেউ কাউকে অপছন্দ করেন না। Decision প্রশ্ন: “k আকারের একটি independent set আছে কি?” এটি NP-তে (certificate হলো সেই k টি vertex, মিলিয়ে দেখতে O(k²) বা O(V + E)). এখন দেখাব 3-SAT ≤p Independent Set.

m টি clause-এর একটি 3-CNF সূত্র φ থেকে গ্রাফ G বানান:

  • প্রতিটি literal-এর জন্য একটি vertex — প্রতিটি clause-এর জন্য তিনটি, মোট 3m.
  • Clause-ত্রিভুজ: একই clause-এর তিনটি vertex পরস্পর যুক্ত। তাই প্রতিটি clause থেকে সর্বোচ্চ একজনকে বাছা যায়।
  • বিরোধের edge: দুটি ভিন্ন clause-এ x আর ¬x থাকলে তাদের মধ্যে edge. তাই একই চলককে একসাথে সত্য আর মিথ্যা “ধরে নেওয়া” যায় না।
  • k = m.
φ = (x1 ∨ x2 ∨ x3) ∧ (¬x1 ∨ ¬x2 ∨ x3) ∧ (x1 ∨ ¬x2 ∨ ¬x3)সবুজ = এই ধাপে নতুন বা বাছা

            
প্রতিটি ধাপের পর
vertex, edge
প্রশ্ন
দিক
  1. ইনপুট. তিনটি চলক, তিনটি clause. প্রতিটি literal-এর জন্য একটি vertex — একই চলক x1 দুই জায়গায় (C1 আর C3) থাকলে দুটি আলাদা vertex.
  2. Clause-ত্রিভুজ. প্রতিটি clause-এর তিনটি vertex পরস্পর যুক্ত (ছবিতে পাশের দুটি edge দেখানো, তৃতীয়টি বন্ধনীতে) — ৩টি ত্রিভুজে ৯টি edge.
  3. x1-এর বিরোধ. x1 আছে C1 আর C3-এ, ¬x1 আছে C2-তে। প্রতিটি জোড়ায় একটি edge — ২টি।
  4. x2-এর বিরোধ. x2 C1-এ, ¬x2 C2 আর C3-এ — আরও ২টি।
  5. x3-এর বিরোধ. x3 C1 আর C2-তে, ¬x3 C3-এ — আরও ২টি। মোট E = 9 + 6 = ১৫, আর k = m = 3. রূপান্তর শেষ: সময় O(m²) — প্রতিটি জোড়া literal একবার দেখলেই।
  6. SAT ⇒ IS. x1 = 1, x2 = 0, x3 = 0 সূত্রটি সত্য করে। প্রতিটি clause থেকে একটি সত্য literal বাছুন: C1.x1, C2.¬x2, C3.x1. তিনটি আলাদা ত্রিভুজে, তাই ত্রিভুজের edge নেই; আর সবগুলোই সত্য literal, অথচ বিরোধের edge থাকে শুধু x আর ¬x-এর মধ্যে, যারা একসাথে সত্য হতে পারে না। তাই এটি ৩ আকারের independent set.
  7. IS ⇒ SAT. উল্টো দিক: ৩ আকারের যেকোনো independent set দিন। ত্রিভুজের কারণে প্রতিটি clause থেকে ঠিক একজন; বিরোধের edge-এর কারণে কোনো চলক x আর ¬x দুটিই বাছা নেই। তাই বাছা literal-গুলো সত্য ধরলে কোনো দ্বন্দ্ব হয় না: x1 = 1, x2 = 0; যে চলক বাছা হয়নি (x3) তার যেকোনো মান। প্রতিটি clause-এ একটি সত্য literal — সূত্রটি সত্য।
  8. k = m কেন. m টি ত্রিভুজ, প্রতিটি থেকে সর্বোচ্চ একজন — তাই কোনো independent set m-এর বড় হতে পারে না। m আকারের set মানে প্রতিটি clause “সন্তুষ্ট”. φ satisfiable ⇔ G-তে m আকারের independent set.
Pseudocode — 3-SAT to Independent Set (clauses 1..m, vertex (j, t) = t-th literal of clause j)

reduce(phi with clauses C1..Cm):
    V = { (j, t) : 1 ≤ j ≤ m, 1 ≤ t ≤ 3 }               // 3m vertices
    for each j: join (j,1)-(j,2), (j,2)-(j,3), (j,1)-(j,3) // clause triangle
    for each pair of vertices in different clauses:
        if their literals are x and NOT x: join them       // conflict edge
    return (G = (V, E), k = m)                             // O(m^2) time

এখন কোডে দুই দিকই brute force দিয়ে মিলিয়ে দেখুন। প্রোগ্রামটি দুটি সূত্র নেয়: F1 = ওপরের φ, আর F2 = তিন চলকের সব ৮টি চিহ্ন-বিন্যাসের clause — প্রতিটি assignment ঠিক একটি clause-কে মিথ্যা করে, তাই F2 unsatisfiable. প্রতিটির জন্য গ্রাফ বানায়, SAT-এর দিকে সব 2ⁿ assignment চেষ্টা করে, গ্রাফের দিকে k আকারের independent set খোঁজে (প্রতিটি vertex নেব/নেব না — পর্ব ৫৬-এর subset গাছ, সাথে “বাকি সব নিলেও k হবে না” সীমা), আর পাওয়া set থেকে assignment ফিরিয়ে আবার verifier দিয়ে মেলায়। x=100 মানে x1 = 1, x2 = 0, x3 = 0; ~x2@C2 মানে C2-এর ¬x2 vertex.

#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
using Formula = vector<vector<int>>;     // literal +i = x_i, -i = NOT x_i
bool satisfied(const Formula& f, int a) {          // verifier: O(m) for m clauses
    for (auto& c : f) {
        bool ok = false;
        for (int l : c) if (((a >> (abs(l) - 1)) & 1) == (l > 0)) ok = true;
        if (!ok) return false;
    }
    return true;
}
int V, K; vector<int> lit; vector<long long> adj; long long nodes;   // vertex 3j+t = t-th literal of clause j
long long findIS(int i, long long chosen, int size) {                // returns the set, or -1
    ++nodes;
    if (size == K) return chosen;
    if (i == V || size + (V - i) < K) return -1;
    if (!(adj[i] & chosen)) { long long r = findIS(i + 1, chosen | 1LL << i, size + 1); if (r >= 0) return r; }
    return findIS(i + 1, chosen, size);
}
string bits(int a, int n) { string s; for (int i = 0; i < n; ++i) s += char('0' + (a >> i & 1)); return s; }
void run(const string& name, const Formula& f, int n) {
    K = f.size(); V = 3 * K; lit.clear(); adj.assign(V, 0); nodes = 0;
    int E = 0; for (auto& c : f) for (int l : c) lit.push_back(l);
    for (int u = 0; u < V; ++u) for (int v = u + 1; v < V; ++v)
        if (u / 3 == v / 3 || lit[u] == -lit[v]) { adj[u] |= 1LL << v; adj[v] |= 1LL << u; ++E; }  // triangle / conflict
    int tries = 0, sat = -1;                                         // side 1: try all 2^n assignments
    for (int a = 0; a < (1 << n); ++a) { ++tries; if (satisfied(f, a)) { sat = a; break; } }
    long long S = findIS(0, 0, 0);                                   // side 2: independent set of size K
    cout << name << ": n=" << n << " clauses=" << K << " -> graph V=" << V << " E=" << E << ", ask: independent set of size " << K << "?\n";
    cout << "  SAT  : " << (sat < 0 ? "no" : "yes x=" + bits(sat, n)) << " (tries=" << tries << ")\n";
    if (S < 0) { cout << "  IS   : no (search nodes=" << nodes << ")\n"; return; }
    cout << "  IS   : yes {"; int b = 0;
    for (int v = 0, first = 1; v < V; ++v) if (S >> v & 1) {
        cout << (first ? "" : " ") << (lit[v] < 0 ? "~x" : "x") << abs(lit[v]) << "@C" << v / 3 + 1; first = 0;
        if (lit[v] > 0) b |= 1 << (lit[v] - 1);                      // chosen literal = true, others false
    }
    cout << "} (search nodes=" << nodes << ")\n  IS -> x=" << bits(b, n) << " satisfies: " << (satisfied(f, b) ? "yes" : "no") << '\n';
}
int main() {
    run("F1", {{1, 2, 3}, {-1, -2, 3}, {1, -2, -3}}, 3);
    run("F2", {{1, 2, 3}, {-1, 2, 3}, {1, -2, 3}, {-1, -2, 3},        // every sign pattern
               {1, 2, -3}, {-1, 2, -3}, {1, -2, -3}, {-1, -2, -3}}, 3);
}

আউটপুট:

F1: n=3 clauses=3 -> graph V=9 E=15, ask: independent set of size 3?
  SAT  : yes x=100 (tries=2)
  IS   : yes {x1@C1 ~x2@C2 x1@C3} (search nodes=8)
  IS -> x=100 satisfies: yes
F2: n=3 clauses=8 -> graph V=24 E=72, ask: independent set of size 8?
  SAT  : no (tries=8)
  IS   : no (search nodes=10799)

F1: ৯টি vertex, ১৫টি edge — ঠিক ওপরের ডায়াগ্রামের মতো। SAT-এর দিকে দ্বিতীয় চেষ্টাতেই 100 পাওয়া যায়, আর গ্রাফের দিকে ৮টি node-এই {C1.x1, C2.¬x2, C3.x1}. সেই set থেকে ফেরানো assignment আবার 100, যা verifier মেনে নেয় — IS ⇒ SAT দিকটি কাজ করছে। F2: ২৪টি vertex, ৭২টি edge (৮টি ত্রিভুজে ২৪টি, আর প্রতিটি চলকের ৪ × ৪ = ১৬টি বিরোধ, তিন চলকে ৪৮)। দুই দিকেই উত্তর “না” — SAT-এ ৮টি assignment-ই ব্যর্থ, আর IS খোঁজা ১০,৭৯৯টি node ঘুরে নিশ্চিত হয় যে ৮ আকারের independent set নেই। লক্ষ করুন, reduction নিজে সস্তা — O(m²) জোড়া দেখা — কিন্তু দুই দিকের খোঁজাই exponential; reduction কোনো সমস্যাকে সহজ করে না, শুধু দেখায় দুটি একই রকম কঠিন। Adjacency রাখা হয়েছে bitmask-এ (adj[i] & chosen একবারে বলে i বাছা কোনো vertex-এর প্রতিবেশী কি না), তাই এই কোড সর্বোচ্চ ৬৩টি vertex-এ চলে।

def satisfied(f, a):                     # verifier: O(m) for m clauses
    return all(any((a >> (abs(l) - 1) & 1) == (l > 0) for l in c) for c in f)

def run(name, f, n):
    k = len(f); V = 3 * k                 # vertex 3j+t = t-th literal of clause j
    lit = [l for c in f for l in c]
    adj = [0] * V; E = 0
    for u in range(V):
        for v in range(u + 1, V):
            if u // 3 == v // 3 or lit[u] == -lit[v]:   # triangle edge / conflict edge
                adj[u] |= 1 << v; adj[v] |= 1 << u; E += 1
    tries, sat = 0, None                  # side 1: try all 2^n assignments
    for a in range(1 << n):
        tries += 1
        if satisfied(f, a):
            sat = a; break
    nodes = 0                             # side 2: search for an independent set of size k
    def find(i, chosen, size):
        nonlocal nodes
        nodes += 1
        if size == k: return chosen
        if i == V or size + (V - i) < k: return None
        if not adj[i] & chosen:
            r = find(i + 1, chosen | 1 << i, size + 1)
            if r is not None: return r
        return find(i + 1, chosen, size)
    S = find(0, 0, 0)
    print(f"{name}: n={n} clauses={k} -> graph V={V} E={E}, ask: independent set of size {k}?")
    print("  SAT  :", "no" if sat is None else "yes x=" + "".join(str(sat >> i & 1) for i in range(n)), f"(tries={tries})")
    if S is None:
        print(f"  IS   : no (search nodes={nodes})")
        return
    print("  IS   : yes {" + " ".join(("~x" if lit[v] < 0 else "x") + str(abs(lit[v])) + f"@C{v // 3 + 1}" for v in range(V) if S >> v & 1) + f"}} (search nodes={nodes})")
    b = 0                                 # chosen literal = true; untouched variables = false
    for v in range(V):
        if S >> v & 1 and lit[v] > 0: b |= 1 << (lit[v] - 1)
    print("  IS -> x=" + "".join(str(b >> i & 1) for i in range(n)), "satisfies:", "yes" if satisfied(f, b) else "no")

run("F1", [[1, 2, 3], [-1, -2, 3], [1, -2, -3]], 3)
run("F2", [[1, 2, 3], [-1, 2, 3], [1, -2, 3], [-1, -2, 3],          # every sign pattern
          [1, 2, -3], [-1, 2, -3], [1, -2, -3], [-1, -2, -3]], 3)

আউটপুট C++-এর সাথে হুবহু এক:

F1: n=3 clauses=3 -> graph V=9 E=15, ask: independent set of size 3?
  SAT  : yes x=100 (tries=2)
  IS   : yes {x1@C1 ~x2@C2 x1@C3} (search nodes=8)
  IS -> x=100 satisfies: yes
F2: n=3 clauses=8 -> graph V=24 E=72, ask: independent set of size 8?
  SAT  : no (tries=8)
  IS   : no (search nodes=10799)

satisfied একটি লাইনে verifier: প্রতিটি clause-এ (all) অন্তত একটি literal (any) সত্য। Literal +i মানে xᵢ, −i মানে ¬xᵢ; assignment একটি পূর্ণসংখ্যা, যার bit i−1 হলো xᵢ. Python-এর পূর্ণসংখ্যার আকার সীমাহীন, তাই এখানে ৬৪-vertex-এর সীমা নেই — কিন্তু খোঁজা তবুও exponential. nonlocal nodes ভেতরের ফাংশনকে বাইরের গণনাটি বদলাতে দেয়। ফেরানো assignment-এ একই চলক দুবার বাছা হলে (এখানে C1 আর C3 দুটিতেই x1) |= একই bit দুবার বসায় — যোগ করলে ভুল হতো।

৬ · Independent set, vertex cover আর clique — একই সমস্যার তিন চেহারা

Independent set NP-complete হয়ে গেলে আরও দুটি সমস্যা প্রায় বিনামূল্যে আসে। একটি গ্রাফ G = (V, E), |V| = n.

  • Independent set ↔ vertex cover: S independent ⇔ V \ S একটি vertex cover. কারণ: S-এর ভেতরে কোনো edge নেই ⇔ প্রতিটি edge-এর অন্তত এক প্রান্ত S-এর বাইরে ⇔ বাইরের অংশ সব edge ছোঁয়। তাই “k আকারের independent set আছে?” ⇔ “n − k আকারের vertex cover আছে?” রূপান্তর: (G, k) ↦ (G, n − k) — O(1)!
  • Independent set ↔ clique: Clique হলো এমন vertex-সেট যার প্রতিটি জোড়া যুক্ত — সবাই সবার বন্ধু। Complement graph Ḡ-তে ঠিক সেই জোড়াগুলো যুক্ত যারা G-তে যুক্ত নয়। S G-তে independent ⇔ S Ḡ-তে clique. রূপান্তর: (G, k) ↦ (Ḡ, k) — O(n²).
গ্রাফ G বা তার complement Ḡসবুজ = এই ধাপের সেট

            
প্রতিটি ধাপের পর
সেট
যাচাই
  1. Independent set. অংশ ২-এর গ্রাফ G. S = {0, 3}: 0 আর 3-এর মধ্যে edge নেই, তাই S independent. ৩ আকারের কোনোটি নেই — যেমন {0, 3, 4}-এ 3–4 edge.
  2. বাকিটা vertex cover. V \ S = {1, 2, 4} — অংশ ২-এর stepper-এ verifier ঠিক এটিকেই বৈধ বলেছিল। কোনো edge-এর দুই প্রান্তই S-এ নেই, তাই প্রতিটি edge-এর অন্তত এক প্রান্ত বাইরে।
  3. Complement graph. ৫টি vertex-এ মোট C(5, 2) = ১০টি জোড়া; G-তে ৬টি edge, তাই Ḡ-তে বাকি ৪টি: 0–3, 0–4, 1–4, 2–3. সাজালে Ḡ একটি পথ: 2 – 3 – 0 – 4 – 1.
  4. Clique. একই S = {0, 3} Ḡ-তে যুক্ত — একটি ২ আকারের clique. Ḡ একটি পথ বলে এতে কোনো ত্রিভুজ নেই, তাই সর্বোচ্চ clique ২ — G-এর সর্বোচ্চ independent set-এর সমান, যেমন হওয়ার কথা।
  5. তিন সমস্যা, এক উত্তর. সর্বোচ্চ independent set k ⇔ সর্বনিম্ন vertex cover n − k ⇔ Ḡ-এ সর্বোচ্চ clique k. একটির দ্রুত সমাধান মানে তিনটিরই। তাই 3-SAT ≤p IS থেকেই vertex cover আর clique দুটিই NP-complete (দুটিই NP-তে — certificate সেটটি নিজে)।

এখানে একটি সূক্ষ্মতা আছে, যা ইন্টারভিউতে প্রায়ই ধরা হয়। সাধারণ গ্রাফে vertex cover NP-complete, কিন্তু bipartite গ্রাফে König-এর উপপাদ্য অনুযায়ী সর্বনিম্ন vertex cover = maximum matching, যা পর্ব ৪৭-এর flow বা Hopcroft–Karp দিয়ে polynomial সময়ে পাওয়া যায়। একইভাবে tree-তে maximum independent set একটি সরল tree DP (পর্ব ৫৫)। “সমস্যাটি NP-hard” কথাটি সবসময় সাধারণ ইনপুটের কথা — বিশেষ গঠনের ইনপুটে সহজ হতেই পারে।

৭ · Hamiltonian cycle → TSP, আর subset sum-এর “প্রায়-polynomial” ফাঁদ

Hamiltonian cycle (প্রতিটি vertex ঠিক একবার ছুঁয়ে শুরুতে ফেরা) Karp-এর তালিকায় NP-complete. এখন TSP-এর decision রূপ: “ওজনসহ complete graph-এ মোট খরচ B বা কম এমন একটি tour আছে কি?” Reduction: Hamiltonian cycle-এর গ্রাফ H (n টি vertex) থেকে complete graph বানান — H-এর edge-এর ওজন 1, বাকি সব জোড়ার ওজন 2; B = n.

H (৪টি vertex) থেকে complete graphসবুজ = এই ধাপের tour বা নতুন জোড়া

            
প্রতিটি ধাপের পর
এই ধাপে
খরচ
  1. ইনপুট H. ৪টি vertex, ৫টি edge: চারটি পাশ আর একটি কোণাকুণি 0–2. প্রশ্ন: Hamiltonian cycle আছে কি?
  2. Complete graph. সব C(4, 2) = ৬টি জোড়া যুক্ত। H-এ যে ৫টি ছিল তাদের ওজন 1; একমাত্র অনুপস্থিত জোড়া 1–3-এর ওজন 2 (ছবির মাঝের ╳ হলো দুই কোণাকুণি 0–2 আর 1–3).
  3. নতুন প্রশ্ন. খরচ ≤ B = n = 4 এমন tour আছে কি? প্রতিটি tour-এ ঠিক n = 4টি edge, প্রতিটির ওজন অন্তত 1 — তাই খরচ ≥ 4 সবসময়।
  4. প্রথম tour. 0 → 1 → 2 → 3 → 0: চারটি edge-ই H-এর, খরচ ঠিক 4 — উত্তর হ্যাঁ, আর এটিই H-এর Hamiltonian cycle.
  5. বাকি tour. ৪টি vertex-এ আলাদা tour মাত্র (4 − 1)!/2 = ৩টি। বাকি দুটি 1–3 ব্যবহার করে, তাই খরচ 5.
  6. প্রমাণ. খরচ ≤ n হতে হলে n টি edge-এর প্রত্যেকটির ওজন 1 হতে হবে, মানে সবগুলো H-এর edge — অর্থাৎ একটি Hamiltonian cycle. উল্টো দিকে H-এর যেকোনো Hamiltonian cycle-এর খরচ ঠিক n. রূপান্তর O(n²). তাই TSP-এর decision রূপ NP-hard, আর NP-তেও (certificate = tour), সুতরাং NP-complete.

এই রূপান্তরের একটি গভীর ফল আছে: ওজন 2-এর জায়গায় খুব বড় একটি সংখ্যা দিলে দেখা যায়, সাধারণ TSP-তে “সর্বোত্তমের কাছাকাছি” উত্তর দেওয়াও NP-hard — যদি না ওজনগুলো triangle inequality মানে। সেই গল্প পর্ব ৬৬-তে।

Subset sum আর knapsack — pseudo-polynomial মানে polynomial নয়

Subset sum (n টি ধনাত্মক সংখ্যার কোনো subset-এর যোগফল ঠিক T কি না) Karp-এর তালিকার knapsack-এর একটি রূপ, আর NP-complete. কিন্তু পর্ব ৫৩-এ আপনি এর একটি O(n · T) DP লিখেছেন! তাহলে কি P = NP প্রমাণ হয়ে গেল? না। “ইনপুটের আকার” মানে ইনপুট লিখতে কতগুলো bit লাগে। T লিখতে লাগে মাত্র প্রায় log₂ T টি bit; ধরুন সেটি b. তাহলে T ≈ 2ᵇ, আর O(n · T) = O(n · 2ᵇ) — ইনপুটের দৈর্ঘ্যে exponential. T = ১০৬ হলে DP দারুণ; কিন্তু T-তে ৩০টি অঙ্ক থাকলে (b ≈ ১০০) টেবিলের আকার ১০৩০-এর ঘরে। এমন সময়কে বলে pseudo-polynomial: সংখ্যাগুলোর মানে polynomial, ইনপুটের দৈর্ঘ্যে নয়। একই কথা 0/1 knapsack-এর O(n · W)-এর জন্যও।

কনটেস্টের ইঙ্গিত: সমস্যায় “n ≤ 100, প্রতিটি ওজন ≤ 1000” লেখা থাকলে মোট ওজন ≤ ১০৫ — pseudo-polynomial DP-ই প্রত্যাশিত সমাধান। “ওজন ≤ 10¹⁸” আর n ≤ 40 দেখলে DP অচল; তখন meet in the middle (দুই ভাগে 2²⁰ করে)। যেসব NP-complete সমস্যার pseudo-polynomial সমাধানও নেই (যদি না P = NP) — যেমন 3-partition, বা সংখ্যাহীন সমস্যা যেমন clique — তাদের বলে strongly NP-hard.

৮ · P = NP? — co-NP, পুরস্কার আর কেন প্রশ্নটি এত গুরুত্বপূর্ণ

co-NP — “না” উত্তরের প্রমাণ

NP-এর সংজ্ঞা অসমমিত: শুধু “হ্যাঁ”-এর ছোট প্রমাণ চাই। “এই সূত্র কখনোই সত্য হয় না” বা “এই গ্রাফে কোনো Hamiltonian cycle নেই” — এর ছোট প্রমাণ কী? সব সম্ভাবনা দেখানো ছাড়া কোনো উপায় জানা নেই। যেসব সমস্যার “না” উত্তরের ছোট, দ্রুত-যাচাইযোগ্য প্রমাণ আছে, তারা co-NP — সমতুল্যভাবে, যাদের complement (হ্যাঁ আর না অদলবদল) NP-তে। UNSAT (সূত্রটি unsatisfiable কি না) co-NP-তে। P ⊆ NP ∩ co-NP, কারণ polynomial সমাধান দুই দিকের উত্তরই দেয়। NP = co-NP কি না, সেটিও অজানা; অনেকে মনে করেন সমান নয়, আর P = NP হলে সমান হতেই হতো। একটি সুন্দর উদাহরণ: “n কি মৌলিক?” প্রশ্নটি NP ∩ co-NP-তে বহু আগেই জানা ছিল (১৯৭৫ সালে Vaughan Pratt দেখান মৌলিকতারও ছোট certificate আছে) — আর ২০০২ সালে Agrawal, Kayal আর Saxena প্রমাণ করেন এটি P-তেই।

প্রশ্নটি আর তার দাম

P = NP? — যা দ্রুত যাচাই করা যায়, তা কি সবসময় দ্রুত খুঁজেও পাওয়া যায়?

২০০০ সালে Clay Mathematics Institute সাতটি “Millennium Prize Problems” ঘোষণা করে, প্রতিটির সমাধানে ১০ লাখ মার্কিন ডলার পুরস্কার; P বনাম NP তার একটি। (সাতটির মধ্যে এখন পর্যন্ত শুধু Poincaré conjecture সমাধান হয়েছে, Grigori Perelman-এর হাতে।) প্রশ্নটি কেন এত গুরুত্বপূর্ণ:

  • যদি P = NP (আর অ্যালগরিদমটি বাস্তবে দ্রুত হয়): সময়সূচি, রুট, chip-এর নকশা, প্রোটিনের গঠনের মতো হাজারো optimization প্রশ্ন দ্রুত সমাধানযোগ্য হতো। গণিতের যেকোনো উপপাদ্যের, যার যুক্তিসংগত দৈর্ঘ্যের প্রমাণ আছে, প্রমাণ খুঁজে পাওয়া যাচাই করার মতোই সহজ হয়ে যেত। আবার আজকের প্রায় সব public-key cryptography ভেঙে পড়ত — গোপন চাবি পেলে যাচাই করা সহজ, তাই “গোপন চাবি খোঁজা” একটি NP প্রশ্ন। bKash লেনদেন থেকে ব্যাংকের ওয়েবসাইট পর্যন্ত নিরাপত্তা এই “খোঁজা কঠিন” অনুমানের ওপর দাঁড়িয়ে।
  • যদি P ≠ NP: হাজারো NP-complete সমস্যার কোনোটিরই polynomial অ্যালগরিদম নেই — তখন আমাদের কাজ approximation, heuristic, বিশেষ গঠন আর exponential কিন্তু চতুর অ্যালগরিদম (পর্ব ৬৫–৬৬)। প্রমাণটি নিজেই সম্ভবত গণনা সম্পর্কে নতুন গভীর ধারণা দেবে।

গবেষকরা কী মনে করেন: William Gasarch ২০০২, ২০১২ আর ২০১৯ সালে গবেষকদের মধ্যে জরিপ করেন; প্রতিবার বেশিরভাগ উত্তরদাতা মনে করেছেন P ≠ NP, আর সেই অংশ সময়ের সাথে বেড়েছে। কিন্তু এটি বিশ্বাস, প্রমাণ নয় — ইতিহাসে বিশেষজ্ঞদের ধারণা ভুলও হয়েছে। নিয়মিতভাবে “P = NP প্রমাণ করেছি” বা “P ≠ NP প্রমাণ করেছি” দাবি আসে; এখন পর্যন্ত কোনোটি বিশেষজ্ঞদের যাচাইয়ে টেকেনি।

মাঝখানে কিছু আছে কি? — NP-intermediate

১৯৭৫ সালে Richard Ladner প্রমাণ করেন: P ≠ NP হলে NP-তে এমন সমস্যা অবশ্যই আছে যা P-তেও নয়, NP-complete-ও নয় — NP-intermediate. কোন স্বাভাবিক সমস্যা এমন, তা নিশ্চিত জানা নেই (জানলে P ≠ NP প্রমাণ হয়ে যেত), কিন্তু দুটি প্রধান সন্দেহভাজন:

  • Integer factorization (decision রূপে: “N-এর কি k-এর চেয়ে ছোট কোনো উৎপাদক আছে?”) — NP ∩ co-NP-তে, তাই NP-complete হলে NP = co-NP হয়ে যেত, যা অনেকে অসম্ভাব্য মনে করেন। RSA-এর নিরাপত্তা এর কঠিনতার ওপর নির্ভর করে। ১৯৯৪ সালে Peter Shor দেখান যে quantum কম্পিউটারে factorization polynomial সময়ে হয় — কিন্তু তাতে NP-complete সমস্যা সহজ হয় এমন কোনো প্রমাণ নেই।
  • Graph isomorphism — দুটি গ্রাফ কি একই, শুধু vertex-এর নাম আলাদা? ২০১৫ সালে László Babai একটি quasi-polynomial (প্রায় n^(log n)-এর ধাঁচের) অ্যালগরিদম ঘোষণা করেন — polynomial নয়, কিন্তু exponential-এর চেয়ে অনেক কম।

আরও গভীরে — NP-এর পরে আরও স্তর: জায়গার (মেমরির) হিসাবে polynomial কিন্তু সময়ে হয়তো exponential সমস্যাগুলো PSPACE — যেমন quantified boolean formula (“সব x-এর জন্য এমন y আছে…” ধরনের সূত্র), বা সাধারণীকৃত Hex খেলায় “এই অবস্থান থেকে কি জেতা নিশ্চিত?”. আরও ওপরে EXP (exponential সময়) — n × n দাবায় একই প্রশ্ন এখানে পড়ে। জানা সম্পর্ক P ⊆ NP ⊆ PSPACE ⊆ EXP, আর time hierarchy theorem থেকে P ≠ EXP — অর্থাৎ এই শিকলের অন্তত একটি ⊆ কঠোর, কিন্তু কোনটি, কেউ জানে না। ১৯৭৫ সালে Baker, Gill আর Solovay দেখান যে শুধু “একটি প্রোগ্রাম দিয়ে আরেকটি চালানো” ধরনের প্রমাণ-কৌশল দিয়ে P বনাম NP মীমাংসা করা যাবে না — প্রশ্নটি কঠিন হওয়ার একটি কারণ এটিই।

৯ · ইন্টারভিউ আর কনটেস্টে NP-hard সমস্যা চেনা

বাস্তবে আপনি প্রায় কখনো NP-completeness-এর আনুষ্ঠানিক প্রমাণ লিখবেন না। কিন্তু একটি সমস্যা পড়ে “এটি আসলে vertex cover / TSP / subset sum-এর ছদ্মবেশ” চিনতে পারা অনেক ঘণ্টা বাঁচায় — আপনি polynomial সমাধান খোঁজা বন্ধ করে সঠিক প্রশ্ন করেন: সীমাগুলো কী বলছে? কনটেস্টের সমস্যা-রচয়িতা NP-hard সমস্যা দিলে সবসময় কোনো একটি ছাড় রাখেন।

সমস্যায় যা দেখবেনসম্ভাব্য পরিচয়প্রত্যাশিত কৌশল
n ≤ 10–12সব ক্রম চেষ্টাpermutation backtracking, O(n · n!) (পর্ব ৫৬)
n ≤ 18–20TSP, Hamiltonian path, assignmentbitmask DP O(2ⁿ · n²) (পর্ব ৫৫)
n ≤ 40subset sum, independent setmeet in the middle, O(2^(n/2) · n)
মান বা ওজন ছোট (≤ ১০৫–১০৬)knapsack, subset sum, partitionpseudo-polynomial DP O(n · W) (পর্ব ৫৩)
গ্রাফটি tree, DAG, bipartite বা intervalindependent set, vertex cover, longest pathtree DP, topological DP, matching/flow (পর্ব ৪২, ৪৭, ৫৫)
প্রতিটি শর্তে মাত্র দুটি চলকSAT-এর মতো দেখায়2-SAT, SCC দিয়ে O(n + m) (পর্ব ৪৬)
k (উত্তরের আকার) ছোট, n বড়vertex cover ≤ kপ্রতিটি edge-এর এক প্রান্ত নিতেই হবে: O(2ᵏ · (n + m)) শাখা-কৌশল
n বড় (১০৫), আর কোনো ছাড় নেইসম্ভবত আপনি ভুল সমস্যা ভাবছেনআবার পড়ুন — greedy বা বিশেষ গঠন লুকিয়ে আছে

শেষ সারিটি সবচেয়ে কাজের। “Longest simple path” NP-hard, কিন্তু DAG-এ longest path O(V + E). “Graph colouring” NP-hard, কিন্তু ২টি রঙে bipartite পরীক্ষা O(V + E). সাধারণ SAT কঠিন, 2-SAT সহজ। সমস্যার নাম শুনে নয়, তার সীমা আর গঠন দেখে সিদ্ধান্ত নিন। আর ইন্টারভিউতে “এর polynomial সমাধান জানা নেই, কারণ এটি vertex cover-এর সমতুল্য; n ছোট হলে bitmask, বড় হলে 2-approximation” — এমন একটি উত্তর প্রায়ই পুরো কোডের চেয়েও বেশি নম্বর আনে।

পরীক্ষা ও ইন্টারভিউতে:

  • NP-hard আর NP-complete-এর পার্থক্য কী? — NP-complete = NP-hard এবং NP-তে; NP-hard সমস্যা NP-এর বাইরেও হতে পারে (optimization রূপ, halting problem).
  • নতুন সমস্যা X NP-hard দেখাতে reduction কোন দিকে? — জানা NP-complete সমস্যা থেকে X-এ (জানা ≤p X), উল্টো দিকে নয়।
  • 0/1 knapsack-এর O(nW) DP থাকা সত্ত্বেও এটি NP-complete কেন? — W-এর মান ইনপুটের bit-দৈর্ঘ্যে exponential; O(nW) pseudo-polynomial.
  • Vertex cover আর independent set-এর সম্পর্ক? — S independent ⇔ V \ S vertex cover; তাই সর্বনিম্ন cover = n − সর্বোচ্চ independent set.
  • NP মানে কি “polynomial সময়ে সমাধান হয় না”? — না; Nondeterministic Polynomial, “দ্রুত যাচাইযোগ্য”; P ⊆ NP.

১০ · AI-এর যুগে complexity — দাবি নয়, reduction যাচাই করুন

AI সহকারীকে একটি নতুন সমস্যা দিলে দুই রকম ভুল প্রায়ই দেখা যায়। এক, একটি আত্মবিশ্বাসী “O(n²) greedy” সমাধান, যা আসলে একটি NP-hard সমস্যার ভুল সমাধান — ছোট উদাহরণে চলে, বড় বা চতুর ইনপুটে ভুল উত্তর দেয়। দুই, উল্টোটা: সহজ একটি সমস্যাকে “এটি NP-hard, তাই brute force ছাড়া উপায় নেই” বলে দেওয়া, যদিও সীমা বা গঠন একটি polynomial পথ খুলে রেখেছে। Reduction-এর প্রমাণেও AI দিক উল্টে ফেলে, বা ⇔-এর এক দিক বাদ দেয়।

AI-এর ভুলকীভাবে ধরবেন
NP-hard সমস্যার “greedy” বা “DP” সমাধান, polynomial দাবি করেছোট এলোমেলো ইনপুটে brute force-এর সাথে stress test; ভুল না পেলেও সমস্যাটি কোনো জানা NP-complete সমস্যার ছদ্মবেশ কি না ভাবুন
“NP মানে non-polynomial” বা “NP = কঠিন”সংজ্ঞায় ফিরুন: verifier আর certificate; P ⊆ NP
Reduction উল্টো দিকে (X ≤p জানা)জিজ্ঞেস করুন: কোন সমস্যার ইনপুট কোনটিতে বদলাচ্ছে?
⇔-এর শুধু এক দিক প্রমাণ“f(x) হ্যাঁ হলে x হ্যাঁ কেন?” আলাদা করে চান
O(n · W) DP-কে polynomial বলাW-এর bit-দৈর্ঘ্যে সময় লিখে দেখুন
বিশেষ গঠন উপেক্ষা (tree, bipartite, 2-SAT)সীমা আর গঠনের তালিকা নিজে লিখে AI-কে দিন

AI-কে উত্তর দিতে না বলে আপনার reduction পরীক্ষা করতে বলুন:

আমি DSA শিখছি, এখন NP-completeness. আমি প্রমাণ করতে চাই যে নিচের সমস্যা X NP-complete. আমার পরিকল্পনা: [জানা NP-complete সমস্যা C] থেকে X-এ reduction, রূপান্তরটি এই: [এখানে লিখুন]। নতুন প্রমাণ লিখে দিও না। শুধু এগুলো করো: ১. reduction-এর দিক ঠিক আছে কি না বলো (C ≤p X হওয়া উচিত)। ২. X ∈ NP দেখাতে আমার certificate আর verifier ঠিক কি না, verifier-এর সময় কত। ৩. "C-এর হ্যাঁ ⇒ X-এর হ্যাঁ" আর "X-এর হ্যাঁ ⇒ C-এর হ্যাঁ" — দুই দিকের যুক্তিতে কোথায় ফাঁক আছে দেখাও। ৪. রূপান্তরটি ইনপুটের bit-দৈর্ঘ্যে polynomial কি না দেখো (বড় সংখ্যা তৈরি হচ্ছে কি না)। ৫. একটি ছোট উদাহরণ দাও যেখানে আমার রূপান্তর ভুল উত্তর দিতে পারে — না পেলে কী কী চেষ্টা করেছ বলো।

Complexity-র দাবি যাচাইয়ের চেকলিস্ট

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

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

প্র ০১১৯৫৬ সালে Gödel-এর একটি চিঠি থেকে Cook আর Levin — P বনাম NP প্রশ্নটি কীভাবে জন্ম নিল?

প্রশ্নটির সবচেয়ে পুরোনো পরিচিত রূপ পাওয়া যায় একটি ব্যক্তিগত চিঠিতে। ১৯৫৬ সালে Kurt Gödel — যিনি ১৯৩১ সালে incompleteness theorem দিয়ে গণিতের ভিত নাড়িয়ে দিয়েছিলেন — অসুস্থ John von Neumann-কে লেখেন। চিঠিতে তিনি জিজ্ঞেস করেন: একটি যুক্তি-সূত্রের n দৈর্ঘ্যের প্রমাণ আছে কি না, তা একটি যন্ত্র কত ধাপে বের করতে পারে? যদি সেটি n বা n²-এর অনুপাতে হতো, তবে গণিতবিদের হ্যাঁ/না প্রশ্নের চিন্তার কাজ যন্ত্রই পুরোপুরি করে দিতে পারত। এটি আজকের ভাষায় প্রায় P বনাম NP. Von Neumann তখন মারাত্মক অসুস্থ ছিলেন, পরের বছর মারা যান; চিঠিটি বহু দশক পরে, ১৯৮০-এর দশকের শেষে, ব্যাপকভাবে পরিচিত হয়।

এর মধ্যে ১৯৬০-এর দশকে Cobham আর Edmonds polynomial সময়কে “দক্ষ” বলার প্রস্তাব দেন, আর Edmonds স্পষ্টভাবে লেখেন যে কিছু সমস্যার “ভালো” অ্যালগরিদম সম্ভবত নেই। ১৯৭১ সালে Stephen Cook টরন্টো থেকে STOC সম্মেলনে দেখান যে SAT-এ NP-এর সব সমস্যা reduce হয়। সোভিয়েত ইউনিয়নে Leonid Levin, Andrey Kolmogorov-এর ছাত্র, একই রকম ফল পান “universal search problem”-এর ভাষায়, যা ১৯৭৩ সালে প্রকাশিত হয়; তিনি নাকি আরও আগে ফলটি জানতেন, কিন্তু প্রকাশে দেরি করেছিলেন। পরে তিনি যুক্তরাষ্ট্রে চলে আসেন। আজ উপপাদ্যটি দুজনের নামেই।

১৯৭২ সালে Karp-এর ২১টি সমস্যা দেখায় যে বাস্তবের scheduling, routing, packing — সব একই পরিবারের। ১৯৭৪ সালে Donald Knuth পরিভাষা ঠিক করতে একটি জরিপ চালান, আর সেখান থেকেই “NP-hard” আর “NP-complete” শব্দগুলো স্থায়ী হয়।

মূল উপলব্ধি: P বনাম NP কোনো একক মুহূর্তের আবিষ্কার নয় — যুক্তিবিদ্যা, অ্যালগরিদম আর বাস্তব optimization-এর তিনটি ধারা মিলে একটি প্রশ্নে পৌঁছেছে, আর তিন দশকে প্রশ্নটি স্পষ্ট রূপ পেয়েছে।

প্র ০২“Polynomial = দক্ষ” সংজ্ঞাটি কোথায় খাটে না — simplex, n¹⁰⁰ আর বাস্তবের SAT solver থেকে কী শিখি?

Cobham–Edmonds thesis একটি সুবিধাজনক রেখা, কিন্তু রেখার দুই পাশেই ব্যতিক্রম আছে। প্রথম ব্যতিক্রম: linear programming-এর simplex পদ্ধতি, যা George Dantzig ১৯৪৭ সালে দেন। ১৯৭২ সালে Victor Klee আর George Minty এমন ইনপুট বানান যেখানে simplex exponential সময় নেয়। অথচ বাস্তবের প্রায় সব ইনপুটে এটি দারুণ দ্রুত, আর আজও শিল্পে ব্যাপকভাবে ব্যবহৃত। ১৯৭৯ সালে Leonid Khachiyan-এর ellipsoid পদ্ধতি প্রথম প্রমাণ করে যে LP polynomial সময়ে হয় — কিন্তু বাস্তবে সেটি ধীর ছিল। ১৯৮৪ সালে Narendra Karmarkar-এর interior-point পদ্ধতি তত্ত্বে polynomial আর বাস্তবেও প্রতিযোগিতামূলক। ২০০১ সালে Spielman আর Teng-এর smoothed analysis ব্যাখ্যা দেয় কেন simplex বাস্তবে ভালো: খারাপ ইনপুটে সামান্য এলোমেলো গোলমাল যোগ করলেই সেগুলো সহজ হয়ে যায়।

দ্বিতীয় ব্যতিক্রম, উল্টো দিকে: কিছু polynomial অ্যালগরিদমের ধ্রুবক বা ঘাত এত বড় যে কখনো ব্যবহার হয় না — একে “galactic algorithm” বলা হয়, যেমন দ্রুততম তাত্ত্বিক matrix multiplication-এর অ্যালগরিদমগুলো। তত্ত্বে তারা জয়ী, বাস্তবে Strassen বা সাধারণ পদ্ধতি।

তৃতীয়: NP-complete SAT নিজেই। আধুনিক CDCL SAT solver লাখো চলকের শিল্প-সমস্যা প্রায়ই সেকেন্ডে মেলায়, কারণ বাস্তবের সূত্রে গঠন থাকে। Worst-case exponential মানে প্রতিটি ইনপুট কঠিন নয়।

তবু রেখাটি টিকে আছে, কারণ এটি একটি গুণগত সত্য ধরে: polynomial অ্যালগরিদম পাওয়া মানে সাধারণত সমস্যার গঠন বোঝা গেছে, আর পরে ঘাত কমানোর পথ খোলে। Exponential মানে আমরা মূলত সব সম্ভাবনা খুঁজছি।

মূল উপলব্ধি: complexity class হলো worst-case আর বৃদ্ধির হারের ভাষা; বাস্তবের গতি জানতে ধ্রুবক, গড় ইনপুট আর ইনপুটের গঠনও দেখতে হয় — দুটি দৃষ্টিভঙ্গি একে অন্যের পরিপূরক।

প্র ০৩যদি কাল কেউ P = NP প্রমাণ করেন — পৃথিবী কি সত্যিই বদলে যাবে?

উত্তর নির্ভর করে প্রমাণটি কেমন তার ওপর। ধরুন প্রমাণটি একটি বাস্তবে দ্রুত অ্যালগরিদম দেয় — ধরুন SAT-এর জন্য O(n³). তাহলে পরিবর্তন বিশাল। Public-key cryptography-র ভিত্তি one-way function: সহজে হিসাব করা যায়, কিন্তু উল্টানো কঠিন। P = NP হলে এমন function থাকতে পারে না, কারণ “এই আউটপুটের ইনপুট কী?” প্রশ্নের উত্তর যাচাই করা সহজ, তাই খোঁজাও সহজ হয়ে যেত। RSA, elliptic curve-ভিত্তিক স্বাক্ষর, অনলাইন ব্যাংকিং আর মোবাইল লেনদেনের নিরাপত্তার এখনকার নকশা বদলাতে হতো। অন্যদিকে, যেকোনো ভালো-সংজ্ঞায়িত optimization — বিমানের সময়সূচি, chip-এর বিন্যাস, ওষুধের অণুর নকশা — অনেক দ্রুত হতো। এমনকি যুক্তিসংগত দৈর্ঘ্যের গাণিতিক প্রমাণ খোঁজাও যন্ত্রের কাজ হয়ে যেত, Gödel ঠিক যা কল্পনা করেছিলেন।

কিন্তু প্রমাণটি হতে পারে অ-গঠনমূলক — “অ্যালগরিদম আছে” দেখায়, কিন্তু কোনটি তা বলে না। অথবা অ্যালগরিদমটি n¹⁰⁰⁰ সময়ের, যা তত্ত্বে polynomial, বাস্তবে অচল। তখন দৈনন্দিন প্রযুক্তি প্রায় একই থাকত, যদিও তত্ত্বের জগতে ভূমিকম্প হতো।

১৯৯৫ সালে Russell Impagliazzo “পাঁচটি সম্ভাব্য পৃথিবী”-র একটি বিখ্যাত বর্ণনা দেন, যেখানে P = NP-এর পৃথিবীতে cryptography প্রায় অসম্ভব, আর অন্য প্রান্তে এমন পৃথিবী যেখানে কঠিন সমস্যা দিয়ে নিরাপদ public-key ব্যবস্থা বানানো যায়। আজকের নিরাপত্তা-ব্যবস্থাগুলো ধরে নেয় আমরা শেষ ধরনের পৃথিবীতে আছি — কিন্তু সেটি প্রমাণিত নয়। মনে রাখবেন, P ≠ NP প্রমাণ হলেও তা নির্দিষ্ট কোনো cryptosystem-কে নিরাপদ প্রমাণ করে না — factorization NP-complete বলে জানা নেই।

মূল উপলব্ধি: P বনাম NP-এর উত্তরের বাস্তব প্রভাব নির্ভর করে প্রমাণটি গঠনমূলক কি না আর অ্যালগরিদমটি কত দ্রুত তার ওপর; “polynomial” আর “বাস্তবে দ্রুত” এক জিনিস নয়।

প্র ০৪মৌলিক সংখ্যা, factorization আর graph isomorphism — যেসব সমস্যা P আর NP-complete-এর মাঝখানে ঝুলে আছে, তাদের গল্প কী?

Ladner-এর ১৯৭৫ সালের উপপাদ্য বলে, P ≠ NP হলে NP-তে মাঝামাঝি সমস্যা থাকতেই হবে। তাঁর প্রমাণে যে সমস্যা তৈরি হয় তা কৃত্রিম — একটি NP-complete সমস্যাকে ইচ্ছা করে “ফুটো” করে বানানো। কিন্তু কয়েকটি প্রাকৃতিক সমস্যা দীর্ঘদিন ধরে এই মাঝের অঞ্চলে বসে আছে বলে মনে হয়।

প্রথম গল্পটি মৌলিকতার, যার শেষ সুখের। “n কি মৌলিক?” বহুদিন ধরে randomized অ্যালগরিদমে (Miller–Rabin, পর্ব ৬১-এর ধাঁচে) বাস্তবে দ্রুত সমাধান হতো, আর ১৯৭৫ সালে Vaughan Pratt দেখান যে এটি NP ∩ co-NP-তে। ২০০২ সালে IIT কানপুরের Manindra Agrawal আর তাঁর দুই স্নাতক ছাত্র Neeraj Kayal ও Nitin Saxena প্রমাণ করেন যে মৌলিকতা P-তে — AKS অ্যালগরিদম। একটি সমস্যা “মাঝে” আছে মনে হওয়া মানে স্থায়ীভাবে মাঝে থাকা নয়।

দ্বিতীয়টি factorization. একটি সংখ্যা মৌলিক কি না বলা সহজ, কিন্তু তার উৎপাদক বের করা কঠিন বলে মনে হয়; RSA-এর নিরাপত্তা ঠিক এই ফাঁকে। এর decision রূপ NP ∩ co-NP-তে, তাই এটি NP-complete হলে NP = co-NP হতো — তাই বেশিরভাগ গবেষক মনে করেন এটি NP-complete নয়। ১৯৯৪ সালে Peter Shor-এর quantum অ্যালগরিদম এটিকে quantum কম্পিউটারে polynomial করেছে, যে কারণে আজ “post-quantum cryptography” নিয়ে কাজ চলছে।

তৃতীয়টি graph isomorphism. বহু দশক এর সবচেয়ে ভালো জানা সময় ছিল প্রায় exp(√(n log n)). ২০১৫ সালে শিকাগো বিশ্ববিদ্যালয়ের László Babai একটি quasi-polynomial অ্যালগরিদম ঘোষণা করেন; ২০১৭ সালে প্রমাণের একটি ত্রুটি পাওয়া যায়, আর তিনি দ্রুতই তা সংশোধন করেন। এটি এখনো polynomial নয়, কিন্তু বাস্তবে প্রায় সব গ্রাফে isomorphism দ্রুতই পরীক্ষা করা যায়।

মূল উপলব্ধি: জটিলতার মানচিত্র শুধু “সহজ” আর “NP-complete” দুই রঙের নয়; মাঝের অঞ্চলের সমস্যাগুলো বারবার নতুন গণিত টেনে এনেছে, আর তাদের কয়েকটির ওপর আজকের নিরাপত্তা দাঁড়িয়ে।

প্র ০৫পঞ্চাশ বছরেও P বনাম NP মীমাংসা হলো না কেন — গবেষকরা কোন “দেয়াল” খুঁজে পেয়েছেন?

P ≠ NP প্রমাণ করা মানে দেখানো যে SAT-এর কোনো polynomial অ্যালগরিদম নেই — শুধু জানা অ্যালগরিদমগুলো নয়, এখনো কেউ কল্পনা করেনি এমনগুলোও। এমন “নিম্নসীমা” প্রমাণ অসাধারণ কঠিন। পর্ব ২৪-এ তুলনা-সর্টের Ω(n log n) সীমা প্রমাণ করেছেন, কিন্তু সেটি একটি সীমিত মডেলে (শুধু তুলনা)। সাধারণ অ্যালগরিদমের জন্য আমরা প্রায় কোনো প্রাকৃতিক NP সমস্যার ক্ষেত্রেই রৈখিকের চেয়ে ভালো নিম্নসীমা প্রমাণ করতে পারিনি।

তার চেয়েও আকর্ষণীয়: গবেষকরা প্রমাণ করেছেন যে কিছু পরিচিত কৌশল এই প্রশ্নে কখনোই কাজ করবে না। ১৯৭৫ সালে Theodore Baker, John Gill আর Robert Solovay দেখান “relativization”-এর বাধা: এমন কাল্পনিক জগৎ আছে যেখানে P = NP, আর এমনও আছে যেখানে P ≠ NP; তাই যে কৌশল সব জগতে একইভাবে খাটে — halting problem-এর diagonalization-এর মতো — তা প্রশ্নটি মীমাংসা করতে পারে না। ১৯৯৪ সালে Alexander Razborov আর Steven Rudich “natural proofs” বাধা দেখান: circuit-এর নিম্নসীমার একটি বড় শ্রেণির প্রমাণ-কৌশল সফল হলে ভালো pseudorandom generator ভেঙে যেত, যা অনেকে অসম্ভাব্য মনে করেন। ২০০৮ সালে Scott Aaronson আর Avi Wigderson “algebrization” নামে তৃতীয় একটি বাধা দেখান।

এর মধ্যে বেশ কয়েকটি বহুল আলোচিত “প্রমাণ” এসেছে — যেমন ২০১০ সালে Vinay Deolalikar-এর P ≠ NP দাবি, যা অনলাইনে বিশেষজ্ঞদের দ্রুত সমবেত পর্যালোচনায় ত্রুটিপূর্ণ প্রমাণিত হয়। এখনকার সবচেয়ে উচ্চাভিলাষী কর্মসূচিগুলোর একটি, Ketan Mulmuley-এর Geometric Complexity Theory, বীজগণিতীয় জ্যামিতির গভীর হাতিয়ার ব্যবহার করে — কিন্তু এর সফলতা বহু দশক দূরে হতে পারে বলে তিনি নিজেই সতর্ক করেছেন।

মূল উপলব্ধি: P বনাম NP কঠিন শুধু এই কারণে নয় যে কেউ যথেষ্ট চেষ্টা করেনি; গবেষকরা প্রমাণ করেছেন যে অনেক স্বাভাবিক পথ এখানে বন্ধ — নতুন ধরনের গণিত লাগবে।

অনুশীলন

খাতা-কলম নিন। প্রথম তিনটি হাতে করার, শেষটিতে কোড লিখতে হবে।

  1. হাতে trace করুন: ψ = (x₁ ∨ ¬x₂ ∨ x₃) ∧ (¬x₁ ∨ x₂ ∨ x₃). অংশ ৫-এর reduction দিয়ে গ্রাফ বানান — কয়টি vertex, কয়টি edge, কোন কোন বিরোধের edge, আর k কত? একটি independent set খুঁজে তা থেকে assignment বের করুন।

    ৬টি vertex (প্রতি clause-এ ৩টি), দুটি ত্রিভুজে ৬টি edge. বিরোধ: C1.x₁ – C2.¬x₁ আর C1.¬x₂ – C2.x₂ — ২টি (x₃ দুই clause-এই একই চিহ্নে, তাই বিরোধ নেই)। মোট ৮টি edge, k = m = 2.

    একটি independent set: {C1.x₃, C2.x₃} — দুটি আলাদা ত্রিভুজে, আর একই literal বলে বিরোধ নেই। Assignment: x₃ = 1, বাকিগুলো যেকোনো মান (ধরুন 0). যাচাই: দুটি clause-এই x₃ সত্য। আরেকটি: {C1.x₁, C2.x₂} → x₁ = 1, x₂ = 1 — C1-এ x₁, C2-তে x₂ সত্য। কিন্তু {C1.x₁, C2.¬x₁} বাছা যাবে না — বিরোধের edge.

  2. ভেবে বলুন: একজন লিখলেন, “আমার সমস্যা ‘পরীক্ষার রুটিন’ (প্রতিটি বিষয় একটি দিনে, একই ছাত্রের দুটি বিষয় একই দিনে নয়, d দিনে হবে কি?) আমি graph colouring-এ বদলে দিয়েছি; colouring NP-complete, তাই আমার সমস্যাও NP-complete.” যুক্তিতে ভুল কোথায়? সঠিক প্রমাণের দিকটি কী?

    তিনি রুটিন ≤p colouring দেখিয়েছেন — এতে শুধু বোঝা যায় রুটিন colouring-এর চেয়ে কঠিন নয়। NP-hard দেখাতে উল্টো দিক লাগে: colouring ≤p রুটিন. সেটি এখানে সহজ: যেকোনো গ্রাফ নিন, প্রতিটি vertex একটি বিষয়, প্রতিটি edge (u, v)-এর জন্য একজন কাল্পনিক ছাত্র যিনি u আর v দুটিই নিয়েছেন, d = রঙের সংখ্যা। গ্রাফটি d রঙে রং করা যায় ⇔ d দিনে রুটিন হয় — রং = দিন। রূপান্তর O(V + E). সাথে রুটিন ∈ NP (certificate = প্রতিটি বিষয়ের দিন, প্রতিটি ছাত্রের বিষয়গুলো মিলিয়ে দেখা) — তবেই NP-complete.

  3. Certificate বলুন: নিচের প্রতিটির জন্য “হ্যাঁ”-এর certificate কী আর verifier কত সময় নেয়? (ক) গ্রাফটি ৩ রঙে রং করা যায়; (খ) n টি সংখ্যার কোনো subset-এর যোগফল T; (গ) গ্রাফে কোনো Hamiltonian cycle নেই; (ঘ) n টি সংখ্যা সাজানো যায় (সবসময় হ্যাঁ)।

    (ক) প্রতিটি vertex-এর রং (0, 1, 2); প্রতিটি edge-এর দুই প্রান্তের রং আলাদা কি না — O(V + E). (খ) subset-টির সূচকগুলো; যোগ করে T-এর সাথে মেলানো — O(n) যোগ, প্রতিটি সংখ্যার bit-দৈর্ঘ্যের অনুপাতে। (গ) কোনো ছোট certificate জানা নেই — এটি co-NP-তে (Hamiltonian cycle-এর complement); NP-তে আছে কি না অজানা, আর থাকলে NP = co-NP হতো। (ঘ) Certificate লাগেই না — verifier নিজেই O(n log n)-এ সাজিয়ে ফেলে; P-এর প্রতিটি সমস্যা এভাবেই NP-তে।

  4. কোড লিখুন: একটি Hamiltonian cycle-এর verifier লিখুন: গ্রাফ (adjacency matrix) আর vertex-এর একটি ক্রম দেওয়া হলে বলুন সেটি বৈধ Hamiltonian cycle কি না। সময় কত? অংশ ৭-এর H-এ 0 1 2 3 আর 0 1 3 2 দিয়ে চালালে কী আসবে?
    bool isHamCycle(const vector<vector<int>>& g, const vector<int>& order) {
        int n = g.size();
        if ((int)order.size() != n) return false;          // must list every vertex
        vector<bool> seen(n, false);
        for (int v : order) {
            if (v < 0 || v >= n || seen[v]) return false;  // out of range or repeated
            seen[v] = true;
        }
        for (int i = 0; i < n; ++i)                        // consecutive pairs, and last back to first
            if (!g[order[i]][order[(i + 1) % n]]) return false;
        return true;
    }

    সময় O(n) (adjacency matrix-এ প্রতিটি edge যাচাই O(1)), জায়গা O(n). H-এ (edge 0–1, 1–2, 2–3, 3–0, 0–2): 0 1 2 3 → true (0–1, 1–2, 2–3, 3–0 সব আছে); 0 1 3 2 → false, কারণ 1–3 edge নেই। খেয়াল করুন তিনটি আলাদা শর্ত: সব vertex একবার করে (দৈর্ঘ্য n আর পুনরাবৃত্তি নেই), পরপর জোড়াগুলো যুক্ত, আর শেষ থেকে প্রথমে ফেরার edge — অনেকে শেষটি ভুলে যান।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি নতুন সমস্যা X NP-complete প্রমাণের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. X-এর জন্য একটি certificate আর polynomial verifier দিয়ে দেখানো যে X ∈ NP
  2. একটি জানা NP-complete সমস্যা C বাছাই করা
  3. C-এর যেকোনো ইনপুটকে X-এর ইনপুটে বদলানোর রূপান্তর f তৈরি করা
  4. দেখানো যে f polynomial সময়ে চলে
  5. প্রমাণ করা যে C-এর উত্তর হ্যাঁ ⇔ f-এর ফলের জন্য X-এর উত্তর হ্যাঁ

NP শ্রেণির সঠিক বর্ণনা কোনটি?

  • যেসব সমস্যা polynomial সময়ে সমাধান করা যায় না
  • যেসব decision problem-এর “হ্যাঁ” উত্তর একটি ছোট certificate দিয়ে polynomial সময়ে যাচাই করা যায়
  • শুধু NP-এর সবচেয়ে কঠিন সমস্যাগুলো
  • যেসব সমস্যা কোনো অ্যালগরিদমেই সমাধান করা যায় না

একটি নতুন সমস্যা X NP-hard প্রমাণ করতে কোন reduction দেখাতে হয়?

  • X ≤p SAT
  • X ≤p একটি P-এর সমস্যা
  • একটি জানা NP-complete সমস্যা C থেকে: C ≤p X
  • X আর একটি NP-complete সমস্যার মধ্যে কোনো রূপান্তর লাগে না, দুটির চেহারা মিললেই হয়

n টি vertex-এর গ্রাফ G-তে S একটি independent set. নিচের কোনটি সবসময় সত্য?

  • V \ S একটি vertex cover, আর complement graph-এ S একটি clique
  • S নিজেই একটি vertex cover
  • S G-তে একটি clique
  • |S| × (সর্বনিম্ন vertex cover-এর আকার) = n

0/1 knapsack-এর O(n · W) DP অ্যালগরিদম (W = ধারণক্ষমতা) থাকা সত্ত্বেও knapsack-এর decision রূপ NP-complete কেন?

  • কারণ DP অ্যালগরিদমটি সব ইনপুটে সঠিক উত্তর দেয় না
  • কারণ এই DP দিয়ে P = NP প্রমাণ হয়ে গেছে
  • কারণ DP-এর সময় আসলে O(n²)
  • কারণ O(n · W) ইনপুটের bit-দৈর্ঘ্যে polynomial নয়, শুধু W-এর মানে polynomial

3-SAT থেকে independent set-এ প্রচলিত reduction-এ m টি clause-এর সূত্রের জন্য গ্রাফে কয়টি vertex থাকে, আর লক্ষ্য k কত?

  • 2n টি vertex (n = চলকের সংখ্যা), k = n
  • 3m টি vertex, k = m
  • m টি vertex, k = m
  • 3m টি vertex, k = 3m

P বনাম NP প্রশ্ন সম্পর্কে কোন বক্তব্যটি সঠিক?

  • ২০০০ সালে প্রমাণিত হয়েছে যে P ≠ NP
  • Integer factorization একটি প্রমাণিত NP-complete সমস্যা
  • P ⊆ NP জানা, কিন্তু P = NP কি না অজানা; এর সমাধানে ১০ লাখ ডলারের পুরস্কার আছে
  • একটি NP-complete সমস্যার polynomial অ্যালগরিদম পেলেও বাকিগুলো কঠিন থেকে যেত

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. P = polynomial সময়ে সমাধানযোগ্য; NP = “হ্যাঁ”-এর certificate polynomial সময়ে যাচাইযোগ্য; P ⊆ NP, আর NP মানে non-polynomial নয়।
  2. A ≤p B: A-এর ইনপুট polynomial সময়ে B-এর ইনপুটে, উত্তর একই (⇔)। B অন্তত A-এর সমান কঠিন; নতুন সমস্যা কঠিন দেখাতে জানা কঠিন সমস্যা থেকে reduce করুন।
  3. NP-complete = NP-তে + NP-hard. Cook (১৯৭১) আর Levin (১৯৭৩): SAT; Karp (১৯৭২): আরও ২১টি।
  4. শিকল: 3-SAT → IS (clause-ত্রিভুজ + বিরোধের edge, k = m); IS k ⇔ VC n − k ⇔ Ḡ-এ clique k; Hamiltonian cycle → TSP (ওজন 1/2, B = n).
  5. O(n · W) pseudo-polynomial, polynomial নয়। P = NP অমীমাংসিত; NP-hard ধাঁচের সমস্যায় সীমা আর বিশেষ গঠন দেখুন।

পরের পর্বে: সমস্যাটি NP-hard বোঝা গেল — এবার কী? বাস্তবে এমন সমস্যা চেনা আর মোকাবিলার কৌশল: ছোট n-এর জন্য exact পদ্ধতি, বিশেষ গঠন, heuristic, SAT ও ILP solver-এর ব্যবহার।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান