UCS
البته! در ادامه، هر بخش و هر خط از کد ارائه شده را به تفصیل توضیح میدهم:
۱. وارد کردن کتابخانهها
import networkx as nx
import matplotlib.pyplot as plt
import heapq
-
import networkx as nx:- NetworkX یک کتابخانه پایتون برای ایجاد، مدیریت و مطالعه ساختار، دینامیک و عملکرد شبکههای پیچیده است. این خط کتابخانه NetworkX را با نام مستعار
nxوارد میکند تا در طول کد به راحتی از آن استفاده شود.
- NetworkX یک کتابخانه پایتون برای ایجاد، مدیریت و مطالعه ساختار، دینامیک و عملکرد شبکههای پیچیده است. این خط کتابخانه NetworkX را با نام مستعار
-
import matplotlib.pyplot as plt:- Matplotlib یک کتابخانه قدرتمند برای ایجاد نمودار و ترسیمهای دو بعدی در پایتون است. ماژول
pyplotآن، که با نام مستعارpltوارد شده، رابطی شبیه به MATLAB برای ایجاد نمودارها فراهم میکند.
- Matplotlib یک کتابخانه قدرتمند برای ایجاد نمودار و ترسیمهای دو بعدی در پایتون است. ماژول
-
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' را پیدا کرده و آن را به صورت تصویری مشاهده کنید.