টপোলজিক্যাল সর্ট
এই পাঠে যা শিখবেন
- টপোলজিক্যাল সর্টের আনুষ্ঠানিক সংজ্ঞা এবং এর অস্তিত্বের শর্ত (DAG হওয়া আবশ্যক)
- Kahn's অ্যালগরিদমের সম্পূর্ণ ইমপ্লিমেন্টেশন (ইন-ডিগ্রি ট্র্যাকিং + BFS queue)
- কেন Kahn's অ্যালগরিদম সাইকেল থাকলে স্বয়ংক্রিয়ভাবে তা সনাক্ত করে
- একটি এক্সপ্লিসিট এজ-চেক লুপ দিয়ে আউটপুট অর্ডারের সঠিকতা যাচাইয়ের পদ্ধতি
১ · টপোলজিক্যাল সর্ট কী এবং কেন এটি গুরুত্বপূর্ণ
একটি ডাইরেক্টেড গ্রাফ $G=(V,E)$-এর একটি টপোলজিক্যাল অর্ডারTopological Orderভার্টেক্সগুলোর একটি লিনিয়ার সিকোয়েন্স যেখানে প্রতিটি ডাইরেক্টেড এজ $u \to v$-এর জন্য $u$, $v$-এর আগে আসে। হলো ভার্টেক্সগুলোর এমন একটি লিনিয়ার সাজানো $v_1, v_2, \ldots, v_n$ যেখানে প্রতিটি এজ $(u,v) \in E$-এর জন্য $u$, $v$-এর আগে আসে। বাস্তব-জীবনের উদাহরণ: কোর্স প্রি-রিকুইজিট (একটি কোর্স নেওয়ার আগে তার প্রি-রিকুইজিট কোর্স শেষ করতে হবে), বিল্ড সিস্টেম ডিপেন্ডেন্সি (একটি ফাইল কম্পাইল করার আগে তার ডিপেন্ডেন্সি কম্পাইল করতে হবে), স্প্রেডশিট সেল ইভ্যালুয়েশন অর্ডার ইত্যাদি।
মূল দাবি: একটি গ্রাফের অন্তত একটি টপোলজিক্যাল অর্ডার থাকে যদি এবং শুধুমাত্র যদি গ্রাফে কোনো ডাইরেক্টেড সাইকেল না থাকে — অর্থাৎ এটি একটি DAG (Directed Acyclic Graph)। এটি স্বজ্ঞাতভাবেই বোঝা যায়: যদি একটি সাইকেল $v_1 \to v_2 \to \cdots \to v_k \to v_1$ থাকে, তাহলে $v_1$-কে $v_2$-এর আগে আসতে হবে (এজ $v_1 \to v_2$), কিন্তু আবার $v_k \to v_1$ এজের কারণে $v_k$-কেও $v_1$-এর আগে আসতে হবে, এবং $v_1,\ldots,v_k$ চেইনের মাধ্যমে $v_1$-কে নিজের আগে আসতে হবে — একটি স্ববিরোধিতা। তাই সাইকেলযুক্ত গ্রাফের কোনো টপোলজিক্যাল অর্ডার থাকতে পারে না।
২ · Kahn's অ্যালগরিদম — ইন-ডিগ্রি ট্র্যাকিং
প্রতিটি ভার্টেক্স $v$-এর ইন-ডিগ্রি হলো তার দিকে আসা এজের সংখ্যা। মূল অন্তর্দৃষ্টি: যদি একটি ভার্টেক্সের ইন-ডিগ্রি ০ হয়, তার মানে কোনো এজ তার আগে থাকার দাবি করছে না — তাই এটিকে নিরাপদে অর্ডারে এখনই বসানো যায়। এটি বসানোর পর, এর আউটগোয়িং এজগুলো "সরিয়ে ফেলা" (এবং সেই প্রতিবেশীদের ইন-ডিগ্রি ১ কমানো) যায়, যা নতুন শূন্য-ইন-ডিগ্রি ভার্টেক্স তৈরি করতে পারে।
যদি সব ভার্টেক্স অর্ডারে যোগ হয়ে যায় (আউটপুট লেন্থ $=|V|$), গ্রাফটি একটি DAG ছিল এবং আমরা একটি বৈধ টপোলজিক্যাল অর্ডার পেয়েছি। কিন্তু যদি প্রক্রিয়া থেমে যাওয়ার পরও কিছু ভার্টেক্স বাকি থেকে যায় (তাদের ইন-ডিগ্রি কখনো ০-তে পৌঁছায়নি), তার মানে সেই বাকি ভার্টেক্সগুলো একটি সাইকেলের অংশ — Kahn's অ্যালগরিদম বাড়তি কাজ ছাড়াই সাইকেল ডিটেকশন বিনামূল্যে দেয়। এই কোর্সে আমরা Kahn's অ্যালগরিদম বেছে নিয়েছি (বিকল্প: DFS-ভিত্তিক ফিনিশ-টাইম রিভার্সাল পদ্ধতি, যা L32-এর কোসারাজু অ্যালগরিদমে ব্যবহৃত ধারণার সাথে সম্পর্কিত)।
৩ · ইমপ্লিমেন্টেশন ও যাচাই — প্রতিটি এজ চেক করা
একটি আউটপুট অর্ডার "সঠিক" প্রমাণ করার সবচেয়ে বিশ্বাসযোগ্য উপায় হলো প্রতিটি এজ $(u,v)$-এর জন্য সরাসরি চেক করা যে অর্ডারে $u$-এর পজিশন $v$-এর পজিশনের আগে কি না — এটি সংজ্ঞা থেকে সরাসরি আসা একটি এক্সহস্টিভ চেক, তাই এটি অ্যালগরিদমের নিজস্ব লজিকের উপর নির্ভর না করেই সঠিকতা প্রমাণ করে।
from collections import deque
def kahn_topo_sort(edges, vertices):
in_degree = {v: 0 for v in vertices}
for u in vertices:
for v in edges.get(u, []):
in_degree[v] += 1
queue = deque([v for v in vertices if in_degree[v] == 0])
order = []
while queue:
u = queue.popleft()
order.append(u)
for v in edges.get(u, []):
in_degree[v] -= 1
if in_degree[v] == 0:
queue.append(v)
return order
def verify_topo_order(edges, order):
# সংজ্ঞা থেকে সরাসরি: প্রতিটি এজ (u, v)-এর জন্য order-এ u অবশ্যই v-এর আগে থাকতে হবে
position = {v: i for i, v in enumerate(order)}
violations = []
for u in edges:
for v in edges[u]:
if position[u] > position[v]:
violations.append((u, v))
return violations
test_dags = [
{ # DAG ১: কোর্স প্রি-রিকুইজিট চেইন
"vertices": ["A", "B", "C", "D", "E", "F"],
"edges": {"A": ["B", "C"], "B": ["D"], "C": ["D", "E"], "D": ["F"], "E": ["F"], "F": []},
},
{ # DAG ২: প্রশস্ত (wide) গ্রাফ, একাধিক ইন-ডিগ্রি-০ ভার্টেক্স একসাথে
"vertices": list(range(8)),
"edges": {0: [1, 2], 1: [3], 2: [3, 4], 3: [5], 4: [5, 6], 5: [7], 6: [7], 7: []},
},
{ # DAG ৩: একাধিক ডিসকানেক্টেড কম্পোনেন্ট + একটি আইসোলেটেড ভার্টেক্স
"vertices": ["x", "y", "z", "w", "iso"],
"edges": {"x": ["y"], "y": ["z"], "z": [], "w": ["z"], "iso": []},
},
]
for i, dag in enumerate(test_dags, 1):
vertices, edges = dag["vertices"], dag["edges"]
order = kahn_topo_sort(edges, vertices)
violations = verify_topo_order(edges, order)
complete = len(order) == len(vertices)
print(f"DAG {i}: order = {order}")
print(f" সব ভার্টেক্স অর্ডারে আছে: {complete} | এজ লঙ্ঘন: {violations if violations else 'নেই'}")
assert complete, f"DAG {i}: কিছু ভার্টেক্স অর্ডারে অনুপস্থিত -- সাইকেল সন্দেহজনক!"
assert not violations, f"DAG {i}: এজ লঙ্ঘন পাওয়া গেছে -- অর্ডার ভুল!"
print("\nসব DAG-এ Kahn's অ্যালগরিদমের আউটপুট প্রতিটি এজ সম্মান করেছে।")
# একটি সাইকেলযুক্ত গ্রাফে Kahn's অ্যালগরিদম যে অসম্পূর্ণ অর্ডার দেয় তা দেখা
cyclic_edges = {"P": ["Q"], "Q": ["R"], "R": ["P"]}
cyclic_vertices = ["P", "Q", "R"]
cyclic_order = kahn_topo_sort(cyclic_edges, cyclic_vertices)
print(f"\nসাইকেলযুক্ত গ্রাফে Kahn's আউটপুট: {cyclic_order} (দৈর্ঘ্য {len(cyclic_order)}, মোট ভার্টেক্স {len(cyclic_vertices)})")
print("সাইকেল ডিটেক্টেড:", len(cyclic_order) < len(cyclic_vertices))
verify_topo_order
যেকোনো বৈধ অর্ডারকে সঠিক হিসেবে গ্রহণ করে, কারণ এটি একটি নির্দিষ্ট অর্ডারের সাথে তুলনা করে না — শুধু এজ কনস্ট্রেইন্ট
মেনে চলা হয়েছে কি না তা চেক করে।
Kahn's অ্যালগরিদমের কমপ্লেক্সিটি $\Theta(V+E)$ (L30-এর BFS-এর মতোই — প্রতিটি ভার্টেক্স ও এজ ঠিক একবার প্রসেস হয়)। এর সঠিকতা এজ-চেক লুপ দিয়ে সরাসরি ভেরিফাই করা সম্ভব, এবং এটি একইসাথে সাইকেল ডিটেকশনও দেয় — এই দ্বৈত ব্যবহারিকতা এটিকে বাস্তব-জীবনের ডিপেন্ডেন্সি-রিজলিউশন সিস্টেমে (প্যাকেজ ম্যানেজার, বিল্ড টুল) খুবই জনপ্রিয় করে তুলেছে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ একই DAG-এর একাধিক বৈধ টপোলজিক্যাল অর্ডার থাকতে পারে কেন — এবং কখন একটি DAG-এর ঠিক একটিই টপোলজিক্যাল অর্ডার থাকবে?
যদি কোনো মুহূর্তে queue-তে একাধিক ইন-ডিগ্রি-০ ভার্টেক্স থাকে, তাদের যেকোনো ক্রমে dequeue করা বৈধ — তাই একাধিক সঠিক অর্ডার সম্ভব। একটি DAG-এর ঠিক একটিই টপোলজিক্যাল অর্ডার থাকবে যদি এবং শুধুমাত্র যদি প্রতিটি ধাপে queue-তে সবসময় ঠিক একটি ভার্টেক্স থাকে — অর্থাৎ গ্রাফে একটি হ্যামিল্টোনিয়ান পাথ থাকে যা প্রতিটি ভার্টেক্স জোড়ার মধ্যে একটি ডাইরেক্ট বা ইনডাইরেক্ট অর্ডারিং সম্পর্ক বাধ্য করে।
প্র ০২ DFS-ভিত্তিক টপোলজিক্যাল সর্ট (ফিনিশ-টাইম রিভার্সাল) কীভাবে কাজ করে, এবং Kahn's-এর তুলনায় এটি কেন সমতুল্য?
DFS-ভিত্তিক পদ্ধতিতে প্রতিটি ভার্টেক্সের উপর DFS চালানো হয় এবং প্রতিটি ভার্টেক্স তার সব প্রতিবেশী সম্পূর্ণ এক্সপ্লোর হওয়ার পর একটি স্ট্যাকে "ফিনিশ" হিসেবে পুশ করা হয় — শেষে স্ট্যাক রিভার্স করলেই টপোলজিক্যাল অর্ডার পাওয়া যায়। এটি সমতুল্য কারণ একটি এজ $u \to v$-এর জন্য $v$ সবসময় $u$-এর আগে ফিনিশ হবে (DAG-তে $v$ $u$-এর সাবট্রিতে থাকতে পারবে না), তাই রিভার্স ফিনিশ-অর্ডারে $u$ সবসময় $v$-এর আগে আসবে।
প্র ০৩ যদি একটি ভার্টেক্সের কোনো ইনকামিং বা আউটগোয়িং এজ না থাকে (সম্পূর্ণ আইসোলেটেড), তাহলে এটি টপোলজিক্যাল অর্ডারে কোথায় থাকতে পারে?
যেকোনো জায়গায় — যেহেতু কোনো এজ এটিকে কোনো অন্য ভার্টেক্সের সাপেক্ষে অবস্থান নিতে বাধ্য করছে না, তাই এটি
অর্ডারের শুরুতে, মাঝে বা শেষে যেকোনো জায়গায় বসালে সব এজ কনস্ট্রেইন্ট মেনে চলবে। উপরের DAG ৩-এর
"iso" ভার্টেক্সটি এর একটি উদাহরণ — Kahn's অ্যালগরিদম এটিকে প্রথম দিকেই queue-তে পাবে (ইন-ডিগ্রি
শুরু থেকেই ০) এবং প্রসেস করার সময় যেখানেই থাকুক তা বৈধ থাকবে।
অনুশীলন
-
চিন্তা করুন: DAG ১-এ যদি একটি নতুন এজ
"F" -> "A"যোগ করা হতো, তাহলেkahn_topo_sort-এর আউটপুট দৈর্ঘ্য কী হতো বলে আপনার ধারণা?এই নতুন এজটি একটি সাইকেল তৈরি করবে (A → B/C → ... → F → A), তাই গ্রাফটি আর DAG থাকবে না — আউটপুট অর্ডারের দৈর্ঘ্য মোট ভার্টেক্স সংখ্যা (৬)-এর চেয়ে কম হবে, কারণ সাইকেলের অংশ ভার্টেক্সগুলোর ইন-ডিগ্রি কখনো ০-তে পৌঁছাবে না।
-
পরীক্ষা করুন: কোড সেলে DAG ১-এর
edgesডিকশনারিতে"F": ["A"]পরিবর্তন করে (মূল খালি লিস্টের বদলে) Run চেপে আপনার অনুমান যাচাই করুন।আউটপুটে দেখা যাবে
order-এর দৈর্ঘ্য ৬-এর কম (মূল ইন-ডিগ্রি-শূন্য ভার্টেক্স "A"-ও আর প্রথমে queue-তে ঢুকবে না, কারণ তার ইন-ডিগ্রি এখন ১), এবংcompleteভ্যারিয়েবলFalseদেখাবে — সাইকেল ডিটেক্টেড।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ SCC, শর্টেস্ট পাথ, ম্যাক্স ফ্লো, ব্যাকট্র্যাকিং, স্ট্রিং অ্যালগরিদম, অ্যামর্টাইজড অ্যানালাইসিস, NP-কমপ্লিটনেস ও অ্যাপ্রক্সিমেশন — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
- Data Structures & Algorithms কোর্স সহোদর কোর্স টপোলজিক্যাল সর্টের মৌলিক ইমপ্লিমেন্টেশন ও প্র্যাকটিস প্রবলেম সেই কোর্সে কভার করা হয়েছে।
- সব 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, Engineering Economics, Full-Stack Web Frameworks, Mobile App Development, Ethics in Computing & AI Safety, Software Testing & Quality Assurance ও Design and Analysis of Algorithms — সব এক জায়গায়।