البته! در ادامه، هر بخش و هر خط از کد ارائه شده را به تفصیل توضیح میدهم:
۱. وارد کردن کتابخانهها
import networkx as nx
import matplotlib.pyplot as plt
import heapq
-
import networkx as nx
:-
NetworkX یک کتابخانه پایتون برای ایجاد، مدیریت و مطالعه ساختار، دینامیک و عملکرد شبکههای پیچیده است. این خط کتابخانه NetworkX را با نام مستعار
nx
وارد میکند تا در طول کد به راحتی از آن استفاده شود.
-
-
import matplotlib.pyplot as plt
:-
Matplotlib یک کتابخانه قدرتمند برای ایجاد نمودار و ترسیمهای دو بعدی در پایتون است. ماژول
pyplot
آن، که با نام مستعارplt
وارد شده، رابطی شبیه به MATLAB برای ایجاد نمودارها فراهم میکند.
-
-
import heapq
:-
heapq یک ماژول استاندارد پایتون است که عملکردهای مربوط به صفهای اولویتدار (heaps) را فراهم میکند. این ماژول برای پیادهسازی صف اولویت در الگوریتمهای جستجو مانند جستجوی هزینه یکنواخت (Uniform Cost Search) استفاده میشود.
-
۲. تعریف کلاس Node
class Node:
def __init__(self, state, parent=None, action=None, path_cost=0):
self.state = state
self.parent = parent
self.action = action
self.path_cost = path_cost
def __lt__(self, other):
return self.path_cost < other.path_cost
-
class Node:
:-
تعریف یک کلاس به نام
Node
که برای نمایش هر گره (Node) در گراف یا درخت جستجو استفاده میشود.
-
-
def __init__(self, state, parent=None, action=None, path_cost=0):
:-
متد سازنده (Constructor) کلاس
Node
است که هنگام ایجاد شیء جدید این کلاس فراخوانی میشود. -
پارامترها:
-
state
: وضعیت یا وضعیت فعلی گره. -
parent
: گره والد (گره قبلی در مسیر). -
action
: عملی که از گره والد به این گره انجام شده است. -
path_cost
: هزینه مسیر از گره شروع تا این گره.
-
-
-
self.state = state
:-
ذخیره وضعیت گره در ویژگی
state
شیء.
-
-
self.parent = parent
:-
ذخیره گره والد در ویژگی
parent
شیء.
-
-
self.action = action
:-
ذخیره عملی که از گره والد به این گره انجام شده است.
-
-
self.path_cost = path_cost
:-
ذخیره هزینه مسیر در ویژگی
path_cost
شیء.
-
-
def __lt__(self, other):
:-
تعریف متد مقایسهای کمتر (
<
) برای اشیاء کلاسNode
. این متد برای مقایسه گرهها بر اساس هزینه مسیرشان استفاده میشود، که برای اولویتبندی در صف اولویتدار ضروری است.
-
-
return self.path_cost < other.path_cost
:-
بازگرداندن نتیجه مقایسه هزینه مسیر این گره با هزینه مسیر گره دیگر.
-
۳. تعریف تابع جستجوی هزینه یکنواخت (Uniform Cost Search)
def uniform_cost_search(graph, start, goal):
frontier = []
heapq.heappush(frontier, Node(start))
explored = set()
path = []
while frontier:
node = heapq.heappop(frontier)
if node.state == goal:
path = reconstruct_path(node)
break
explored.add(node.state)
for (cost, result_state) in graph[node.state]:
if result_state not in explored:
child_cost = node.path_cost + cost
child_node = Node(result_state, node, None, child_cost)
if not any(frontier_node.state == result_state and
frontier_node.path_cost <= child_cost for frontier_node in frontier):
heapq.heappush(frontier, child_node)
return path
این تابع برای پیدا کردن کمترین هزینه مسیر از نقطه شروع (start
) تا مقصد (goal
) در یک گراف استفاده میشود.
-
def uniform_cost_search(graph, start, goal):
:-
تعریف تابع
uniform_cost_search
با پارامترهای:-
graph
: گراف ورودی که به صورت یک دیکشنری پیادهسازی شده است. -
start
: گره شروع جستجو. -
goal
: گره هدف یا مقصد جستجو.
-
-
-
frontier = []
:-
ایجاد یک لیست خالی به نام
frontier
که نقش صف اولویتدار را بازی میکند. این لیست برای نگهداری گرههایی است که باید بررسی شوند.
-
-
heapq.heappush(frontier, Node(start))
:-
ایجاد یک شیء
Node
با وضعیت اولیهstart
و افزودن آن بهfrontier
با استفاده از تابعheappush
از ماژولheapq
. ابتدا، هزینه مسیر این گره صفر است.
-
-
explored = set()
:-
ایجاد یک مجموعه خالی به نام
explored
برای ذخیره وضعیت گرههایی که قبلاً بررسی شدهاند تا از بازبینی مجدد آنها جلوگیری شود.
-
-
path = []
:-
ایجاد یک لیست خالی به نام
path
که مسیر نهایی ازstart
بهgoal
در آن ذخیره میشود.
-
-
while frontier:
:-
آغاز یک حلقه
while
که تا زمانی کهfrontier
خالی نباشد اجرا میشود.
-
-
node = heapq.heappop(frontier)
:-
خارج کردن گره با کمترین هزینه مسیر از
frontier
با استفاده ازheappop
و ذخیره آن در متغیرnode
.
-
-
if node.state == goal:
:-
بررسی اینکه آیا وضعیت گره فعلی (
node.state
) برابر با وضعیت هدف (goal
) است.
-
-
path = reconstruct_path(node)
:-
اگر گره فعلی هدف باشد، مسیر از طریق تابع
reconstruct_path
بازسازی میشود و نتیجه درpath
ذخیره میگردد.
-
-
break
:-
خروج از حلقه
while
چون مسیر به مقصد پیدا شده است.
-
-
explored.add(node.state)
:-
افزودن وضعیت گره فعلی به مجموعه
explored
برای نشان دادن این که این گره بررسی شده است.
-
-
for (cost, result_state) in graph[node.state]:
:-
مرور تمام همسایگان (شامل هزینه و وضعیت) گره فعلی در گراف.
-
-
if result_state not in explored:
:-
بررسی اینکه وضعیت همسایه قبلاً بررسی نشده باشد.
-
-
child_cost = node.path_cost + cost
:-
محاسبه هزینه مسیر به همسایه با افزودن هزینه یال جاری (
cost
) به هزینه مسیر جاری (node.path_cost
).
-
-
child_node = Node(result_state, node, None, child_cost)
:-
ایجاد یک شیء
Node
جدید برای همسایه با وضعیتresult_state
, والد آنnode
, عملی مشخص نشده (None
)، و هزینه مسیر محاسبه شدهchild_cost
.
-
-
if not any(frontier_node.state == result_state and frontier_node.path_cost <= child_cost for frontier_node in frontier):
:-
بررسی اینکه آیا همسایه با هزینه مسیر کمتر یا مساوی در
frontier
وجود دارد یا خیر. اگر وجود نداشته باشد، همسایه جدید بهfrontier
افزوده میشود.
-
-
heapq.heappush(frontier, child_node)
:-
افزودن همسایه جدید به
frontier
با استفاده ازheappush
تا در صف اولویت قرار گیرد.
-
-
return path
:-
بازگرداندن مسیر نهایی پیدا شده پس از پایان حلقه
while
.
-
۴. تعریف تابع بازسازی مسیر (Reconstruct Path)
def reconstruct_path(node):
path = []
while node:
path.append(node.state)
node = node.parent
return path[::-1]
این تابع برای بازسازی مسیر از گره هدف به گره شروع استفاده میشود.
-
def reconstruct_path(node):
:-
تعریف تابع
reconstruct_path
که یک شیءNode
(فرضاً گره هدف) را به عنوان ورودی میگیرد.
-
-
path = []
:-
ایجاد یک لیست خالی به نام
path
برای ذخیره مسیر.
-
-
while node:
:-
آغاز یک حلقه
while
تا زمانی کهnode
برابر باNone
نباشد.
-
-
path.append(node.state)
:-
افزودن وضعیت گره فعلی به لیست
path
.
-
-
node = node.parent
:-
حرکت به گره والد برای ادامه بازسازی مسیر به سمت گره شروع.
-
-
return path[::-1]
:-
بازگرداندن مسیر به صورت معکوس (
[::-1]
)، زیرا مسیر از گره هدف به گره شروع ساخته شده است و نیاز به ترتیب از شروع به هدف دارد.
-
۵. تعریف تابع ترسیم گراف (Visualize Graph)
def visualize_graph(graph, path=None):
G = nx.DiGraph()
labels = {}
for node, edges in graph.items():
for cost, child_node in edges:
G.add_edge(node, child_node, weight=cost)
labels[(node, child_node)] = cost
pos = nx.spring_layout(G)
nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=2000)
nx.draw_networkx_edge_labels(G, pos, edge_labels=labels)
if path:
path_edges = list(zip(path[:-1], path[1:]))
nx.draw_networkx_edges(G, pos, edgelist=path_edges, edge_color='r', width=2)
plt.show()
این تابع برای ترسیم گراف و مسیر یافت شده (در صورت وجود) استفاده میشود.
-
def visualize_graph(graph, path=None):
:-
تعریف تابع
visualize_graph
با پارامترهای:-
graph
: گراف ورودی به صورت دیکشنری. -
path
: مسیر پیدا شده توسط الگوریتم جستجو (اختیاری).
-
-
-
G = nx.DiGraph()
:-
ایجاد یک گراف جهتدار خالی با استفاده از NetworkX.
-
-
labels = {}
:-
ایجاد یک دیکشنری خالی برای ذخیره برچسبهای یالها (هزینهها).
-
-
for node, edges in graph.items():
:-
مرور هر گره و یالهای متصل به آن در گراف ورودی.
-
-
for cost, child_node in edges:
:-
مرور هر یال متصل به گره جاری شامل هزینه یال و گره مقصد.
-
-
G.add_edge(node, child_node, weight=cost)
:-
افزودن یال به گراف NetworkX با وزن (هزینه) مشخص.
-
-
labels[(node, child_node)] = cost
:-
ذخیره هزینه یال به عنوان برچسب در دیکشنری
labels
.
-
-
pos = nx.spring_layout(G)
:-
محاسبه موقعیت هر گره در فضای دو بعدی با استفاده از الگوریتم نمایش فنری (Spring Layout) برای جذابیت بصری.
-
-
nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=2000)
:-
ترسیم گراف با موقعیتهای تعیینشده، نمایش برچسبها، رنگ دهی گرهها به رنگ آبی روشن و تنظیم اندازه گرهها.
-
-
nx.draw_networkx_edge_labels(G, pos, edge_labels=labels)
:-
ترسیم برچسبهای یالها (هزینهها) در موقعیتهای تعیینشده.
-
-
if path:
:-
بررسی اینکه آیا مسیر پیدا شده (
path
) وجود دارد یا خیر.
-
-
path_edges = list(zip(path[:-1], path[1:]))
:-
ایجاد لیستی از یالهای مسیر به صورت جفتهای پشت سر هم.
-
-
nx.draw_networkx_edges(G, pos, edgelist=path_edges, edge_color='r', width=2)
:-
ترسیم یالهای مسیر با رنگ قرمز و عرض دو برابر برای برجسته کردن مسیر.
-
-
plt.show()
:-
نمایش نمودار ترسیمشده با استفاده از Matplotlib.
-
۶. تعریف گراف، نقطه شروع و هدف، اجرای جستجو و نمایش نتایج
# Define the graph
graph = {
'A': [(1, 'B'), (3, 'C')],
'B': [(2, 'D')],
'C': [(5, 'D'), (2, 'B')],
'D': []
}
start = 'A'
goal = 'D'
path = uniform_cost_search(graph, start, goal)
visualize_graph(graph, path)
print("Path from", start, "to", goal, ":", path)
-
تعریف گراف:
graph = { 'A': [(1, 'B'), (3, 'C')], 'B': [(2, 'D')], 'C': [(5, 'D'), (2, 'B')], 'D': [] }
-
گراف به صورت یک دیکشنری تعریف شده است که کلیدها گرهها (
'A'
,'B'
,'C'
,'D'
) و مقادیر لیستی از تاپلها هستند. هر تاپل شامل یک هزینه یال و گره مقصد است. -
به عنوان مثال، از گره
'A'
به'B'
با هزینه1
و به'C'
با هزینه3
ارتباط وجود دارد. -
گره
'D'
هیچ یالی خروجی ندارد (لیست خالی).
-
-
تعریف نقطه شروع و هدف:
start = 'A' goal = 'D'
-
نقطه شروع جستجو گره
'A'
و هدف جستجو گره'D'
است.
-
-
اجرای جستجوی هزینه یکنواخت:
path = uniform_cost_search(graph, start, goal)
-
فراخوانی تابع
uniform_cost_search
با گراف تعریفشده، نقطه شروع'A'
و هدف'D'
. نتیجه مسیر پیدا شده در متغیرpath
ذخیره میشود.
-
-
ترسیم گراف و مسیر:
visualize_graph(graph, path)
-
فراخوانی تابع
visualize_graph
با گراف ورودی و مسیر پیدا شده. این تابع گراف را ترسیم کرده و مسیر پیدا شده را برجسته میکند.
-
-
چاپ مسیر پیدا شده:
print("Path from", start, "to", goal, ":", path)
-
نمایش مسیر پیدا شده از نقطه
'A'
به'D'
به صورت متنی در کنسول. برای مثال:Path from A to D : ['A', 'B', 'D']
-
خلاصه عملکرد کلی کد
-
ایمپورت کتابخانههای لازم: برای مدیریت گراف، ترسیم و استفاده از صف اولویت.
-
تعریف کلاس
Node
: برای نمایش هر گره در روند جستجو با ویژگیهایی مانند وضعیت، والد، عمل و هزینه مسیر. -
تعریف تابع
uniform_cost_search
: پیادهسازی الگوریتم جستجوی هزینه یکنواخت برای پیدا کردن کمترین هزینه مسیر از نقطه شروع به هدف. -
تعریف تابع
reconstruct_path
: بازسازی مسیر پیدا شده از گره هدف به گره شروع. -
تعریف تابع
visualize_graph
: ترسیم گراف و مسیر پیدا شده به صورت بصری. -
تعریف گراف نمونه: یک گراف ساده با چهار گره و هزینههای مشخص برای یالها.
-
اجرای جستجو: پیدا کردن مسیر از
'A'
به'D'
با استفاده از الگوریتم. -
نمایش نتایج: ترسیم گراف با مسیر پیدا شده و چاپ مسیر در کنسول.
با اجرای این کد، شما میتوانید مسیر با کمترین هزینه از گره 'A'
به گره 'D'
را پیدا کرده و آن را به صورت تصویری مشاهده کنید.