Python yordamida eng qisqa yo'l algoritmlarini amalga oshirish bo'yicha keng qamrovli qo'llanma, Dijkstra, Bellman-Ford va A* qidiruvini qamrab oladi. Amaliy misollar va kod parchalarini o'rganing.
Python Graph Algoritmlari: Eng Qisqa Yoʻl Yechimlarini Amalga Oshirish
Grafiklar kompyuter fanida ob'ektlar o'rtasidagi munosabatlarni modellashtirish uchun ishlatiladigan fundamental ma'lumotlar tuzilmalaridir. Grafikdagi ikkita nuqta orasidagi eng qisqa yo'lni topish GPS navigatsiyasidan tortib tarmoq marshrutlash va resurslarni taqsimlashgacha bo'lgan ilovalarga ega bo'lgan umumiy muammodir. Python, o'zining boy kutubxonalari va aniq sintaksisi bilan grafik algoritmlarini amalga oshirish uchun ajoyib tildir. Ushbu keng qamrovli qo'llanma turli xil eng qisqa yo'l algoritmlarini va ularning Python ilovalarini o'rganadi.
Grafiklarni tushunish
Algoritmlarga sho'ng'ishdan oldin, grafik nima ekanligini aniqlab olaylik:
- Tugunlar (uchlar): Ob'ektlar yoki entitetlarni ifodalaydi.
- Qirralar: Tugunlarni bog'laydi, ular o'rtasidagi munosabatlarni ifodalaydi. Qirralar yo'naltirilgan (bir tomonlama) yoki yo'naltirilmagan (ikki tomonlama) bo'lishi mumkin.
- Og'irliklar: Qirralar narxni, masofani yoki boshqa tegishli ko'rsatkichni ifodalovchi og'irliklarga ega bo'lishi mumkin. Agar hech qanday og'irlik ko'rsatilmagan bo'lsa, u ko'pincha 1 deb taxmin qilinadi.
Grafiklarni Python-da turli xil ma'lumotlar tuzilmalari, masalan, qo'shnilik ro'yxatlari va qo'shnilik matritsalari yordamida ifodalash mumkin. Biz misollarimiz uchun qo'shnilik ro'yxatidan foydalanamiz, chunki u ko'pincha siyrak grafiklar (nisbatan kam qirralarga ega bo'lgan grafiklar) uchun samaraliroqdir.
Python-da grafikni qo'shnilik ro'yxati sifatida ifodalash namunasi:
graph = {
'A': [('B', 5), ('C', 2)],
'B': [('D', 4)],
'C': [('B', 8), ('D', 7)],
'D': [('E', 6)],
'E': []
}
Ushbu misolda grafik A, B, C, D va E tugunlariga ega. Har bir tugun bilan bog'liq qiymat - bu boshqa tugunga qirrani va bu qirraning og'irligini ifodalovchi kortejlarning ro'yxati.
Dijkstra Algoritmi
Kirish
Dijkstra algoritmi - manba tugunidan grafikdagi barcha boshqa tugunlarga manfiy bo'lmagan qirrali og'irliklar bilan eng qisqa yo'lni topish uchun klassik algoritm. Bu grafikni iterativ ravishda o'rganadigan ochko'z algoritm, har doim manbadan eng kichik ma'lum masofaga ega bo'lgan tugunni tanlaydi.
Algoritm qadamlari
- Manbadan har bir tugungacha bo'lgan eng qisqa masofani saqlash uchun lug'atni initsializatsiya qiling. Manba tuguniga masofani 0 ga va boshqa barcha tugunlarga masofani cheksizlikka o'rnating.
- Tashrif buyurilgan tugunlar to'plamini bo'sh bo'lishi uchun initsializatsiya qiling.
- Tashrif buyurilmagan tugunlar mavjud bo'lganda:
- Manbadan eng kichik ma'lum masofaga ega bo'lgan tashrif buyurilmagan tugunni tanlang.
- Tanlangan tugunni tashrif buyurilgan deb belgilang.
- Tanlangan tugunning har bir qo'shnisi uchun:
- Tanlangan tugun orqali manbadan qo'shnigacha bo'lgan masofani hisoblang.
- Agar bu masofa qo'shniga hozirgi ma'lum masofadan qisqaroq bo'lsa, qo'shnining masofasini yangilang.
- Manbadan barcha boshqa tugunlarga eng qisqa masofalar endi ma'lum.
Python-da amalga oshirish
import heapq
def dijkstra(graph, start):
distances = {node: float('inf') for node in graph}
distances[start] = 0
priority_queue = [(0, start)] # (distance, node)
while priority_queue:
distance, node = heapq.heappop(priority_queue)
if distance > distances[node]:
continue # Already processed a shorter path to this node
for neighbor, weight in graph[node]:
new_distance = distance + weight
if new_distance < distances[neighbor]:
distances[neighbor] = new_distance
heapq.heappush(priority_queue, (new_distance, neighbor))
return distances
# Example usage:
graph = {
'A': [('B', 5), ('C', 2)],
'B': [('D', 4)],
'C': [('B', 8), ('D', 7)],
'D': [('E', 6)],
'E': []
}
start_node = 'A'
shortest_distances = dijkstra(graph, start_node)
print(f"Shortest distances from {start_node}: {shortest_distances}")
Misol izohi
Kod eng kichik masofaga ega bo'lgan tashrif buyurilmagan tugunni samarali tanlash uchun ustuvor navbatdan (`heapq` bilan amalga oshirilgan) foydalanadi. `distances` lug'ati boshlang'ich tugundan har bir boshqa tugungacha bo'lgan eng qisqa masofani saqlaydi. Algoritm barcha tugunlarga tashrif buyurilgunga qadar (yoki erishib bo'lmaydigan bo'lgunga qadar) bu masofalarni iterativ ravishda yangilab turadi.
Murakkablik tahlili
- Vaqt murakkabligi: O((V + E) log V), bu erda V - uchlar soni va E - qirralar soni. Log V omili heap operatsiyalaridan kelib chiqadi.
- Fazo murakkabligi: O(V), masofalarni va ustuvor navbatni saqlash uchun.
Bellman-Ford Algoritmi
Kirish
Bellman-Ford algoritmi - bu grafikdagi barcha boshqa tugunlarga bitta manba tugunidan eng qisqa yo'lni topish uchun yana bir algoritm. Dijkstra algoritmida farqli o'laroq, u manfiy qirrali og'irliklari bo'lgan grafiklar bilan ishlay oladi. Biroq, u manfiy tsikllari bo'lgan grafiklar bilan ishlay olmaydi (qirrali og'irliklar yig'indisi manfiy bo'lgan tsikllar), chunki bu yo'l uzunligining cheksiz kamayishiga olib keladi.
Algoritm qadamlari
- Manbadan har bir tugungacha bo'lgan eng qisqa masofani saqlash uchun lug'atni initsializatsiya qiling. Manba tuguniga masofani 0 ga va boshqa barcha tugunlarga masofani cheksizlikka o'rnating.
- Quyidagi qadamlarni V-1 marta takrorlang, bu erda V - uchlar soni:
- Grafikdagi har bir (u, v) qirrasi uchun:
- Agar u gacha bo'lgan masofa va (u, v) qirrasi og'irligi v gacha bo'lgan joriy masofadan kam bo'lsa, v gacha bo'lgan masofani yangilang.
- V-1 iteratsiyadan so'ng manfiy tsikllarni tekshiring. Grafikdagi har bir (u, v) qirrasi uchun:
- Agar u gacha bo'lgan masofa va (u, v) qirrasi og'irligi v gacha bo'lgan joriy masofadan kam bo'lsa, unda manfiy tsikl mavjud.
- Agar manfiy tsikl aniqlansa, algoritm tugaydi va uning mavjudligi haqida xabar beradi. Aks holda, manbadan barcha boshqa tugunlarga eng qisqa masofalar ma'lum.
Python-da amalga oshirish
def bellman_ford(graph, start):
distances = {node: float('inf') for node in graph}
distances[start] = 0
# Relax edges repeatedly
for _ in range(len(graph) - 1):
for node in graph:
for neighbor, weight in graph[node]:
if distances[node] != float('inf') and distances[node] + weight < distances[neighbor]:
distances[neighbor] = distances[node] + weight
# Check for negative cycles
for node in graph:
for neighbor, weight in graph[node]:
if distances[node] != float('inf') and distances[node] + weight < distances[neighbor]:
return "Negative cycle detected"
return distances
# Example usage:
graph = {
'A': [('B', -1), ('C', 4)],
'B': [('C', 3), ('D', 2), ('E', 2)],
'C': [],
'D': [('B', 1), ('C', 5)],
'E': [('D', -3)]
}
start_node = 'A'
shortest_distances = bellman_ford(graph, start_node)
print(f"Shortest distances from {start_node}: {shortest_distances}")
Misol izohi
Kod grafikdagi barcha qirralarni V-1 marta iteratsiya qiladi, agar qisqaroq yo'l topilsa, ularni bo'shashtiradi (masofalarni yangilaydi). V-1 iteratsiyadan so'ng, u qirralarni yana bir marta iteratsiya qilish orqali manfiy tsikllarni tekshiradi. Agar biron bir masofa hali ham kamaytirilsa, bu manfiy tsikl mavjudligini bildiradi.
Murakkablik tahlili
- Vaqt murakkabligi: O(V * E), bu erda V - uchlar soni va E - qirralar soni.
- Fazo murakkabligi: O(V), masofalarni saqlash uchun.
A* Qidiruv Algoritmi
Kirish
A* qidiruv algoritmi - yo'l topish va grafikni kesib o'tish uchun keng qo'llaniladigan xabardor qidiruv algoritmi. U boshlang'ich tugundan maqsad tugungacha eng qisqa yo'lni samarali topish uchun Dijkstra algoritmi va evristik qidiruv elementlarini birlashtiradi. A* muammo sohasida qidiruvni yo'naltirish uchun ishlatilishi mumkin bo'lgan ba'zi bilimga ega bo'lgan vaziyatlarda ayniqsa foydalidir.
Evristik funksiya
A* qidiruvining kaliti - bu h(n) deb belgilangan evristik funksiyadan foydalanish, bu berilgan n tugunidan maqsad tugungacha yetib borish narxini taxmin qiladi. Evristika qabul qilinishi kerak, ya'ni u hech qachon haqiqiy narxni ortiqcha baholamaydi. Umumiy evristikalar Evklid masofasi (to'g'ri chiziq masofasi) yoki Manhattan masofasi (koordinatalardagi mutlaq farqlar yig'indisi) ni o'z ichiga oladi.
Algoritm qadamlari
- Boshlang'ich tugunni o'z ichiga olgan ochiq to'plamni initsializatsiya qiling.
- Yopiq to'plamni bo'sh bo'lishi uchun initsializatsiya qiling.
- Boshlang'ich tugundan har bir tugungacha bo'lgan narxni (g(n)) saqlash uchun lug'atni initsializatsiya qiling. Boshlang'ich tuguniga narxni 0 ga va boshqa barcha tugunlarga narxni cheksizlikka o'rnating.
- Har bir tugun orqali boshlang'ich tugundan maqsad tugungacha bo'lgan taxminiy umumiy narxni saqlash uchun lug'atni initsializatsiya qiling (f(n) = g(n) + h(n)).
- Ochiq to'plam bo'sh bo'lmaganda:
- Ochiq to'plamdagi eng past f(n) qiymatiga ega bo'lgan tugunni (eng istiqbolli tugun) tanlang.
- Agar tanlangan tugun maqsad tugun bo'lsa, yo'lni qayta tiklang va qaytaring.
- Tanlangan tugunni ochiq to'plamdan yopiq to'plamga o'tkazing.
- Tanlangan tugunning har bir qo'shnisi uchun:
- Agar qo'shni yopiq to'plamda bo'lsa, uni o'tkazib yuboring.
- Tanlangan tugun orqali boshlang'ich tugundan qo'shnigacha yetib borish narxini hisoblang.
- Agar qo'shni ochiq to'plamda bo'lmasa yoki yangi narx qo'shniga joriy narxdan pastroq bo'lsa:
- Qo'shniga narxni yangilang (g(n)).
- Qo'shni orqali maqsadga taxminiy umumiy narxni yangilang (f(n)).
- Agar qo'shni ochiq to'plamda bo'lmasa, uni ochiq to'plamga qo'shing.
- Agar ochiq to'plam bo'sh bo'lib qolsa va maqsad tuguniga yetib borilmagan bo'lsa, boshlang'ich tugundan maqsad tugungacha yo'l yo'q.
Python-da amalga oshirish
import heapq
def a_star(graph, start, goal, heuristic):
open_set = [(0, start)] # (f_score, node)
closed_set = set()
g_score = {node: float('inf') for node in graph}
g_score[start] = 0
f_score = {node: float('inf') for node in graph}
f_score[start] = heuristic(start, goal)
came_from = {}
while open_set:
f, current_node = heapq.heappop(open_set)
if current_node == goal:
return reconstruct_path(came_from, current_node)
closed_set.add(current_node)
for neighbor, weight in graph[current_node]:
if neighbor in closed_set:
continue
tentative_g_score = g_score[current_node] + weight
if tentative_g_score < g_score[neighbor]:
came_from[neighbor] = current_node
g_score[neighbor] = tentative_g_score
f_score[neighbor] = tentative_g_score + heuristic(neighbor, goal)
if (f_score[neighbor], neighbor) not in open_set:
heapq.heappush(open_set, (f_score[neighbor], neighbor))
return None # No path found
def reconstruct_path(came_from, current_node):
path = [current_node]
while current_node in came_from:
current_node = came_from[current_node]
path.append(current_node)
path.reverse()
return path
# Example Heuristic (Euclidean distance for demonstration, graph nodes should have x, y coords)
def euclidean_distance(node1, node2):
# This example requires the graph to store coordinates with each node, such as:
# graph = {
# 'A': [('B', 5), ('C', 2)],
# 'B': [('D', 4)],
# 'C': [('B', 8), ('D', 7)],
# 'D': [('E', 6)],
# 'E': [],
# 'coords': {
# 'A': (0, 0),
# 'B': (3, 4),
# 'C': (1, 1),
# 'D': (5, 2),
# 'E': (7, 0)
# }
# }
#
# Since we don't have coordinates in the default graph, we'll just return 0 (admissible)
return 0
# Replace this with your actual distance calculation if nodes have coordinates:
# x1, y1 = graph['coords'][node1]
# x2, y2 = graph['coords'][node2]
# return ((x1 - x2)**2 + (y1 - y2)**2)**0.5
# Example Usage:
graph = {
'A': [('B', 5), ('C', 2)],
'B': [('D', 4)],
'C': [('B', 8), ('D', 7)],
'D': [('E', 6)],
'E': []
}
start_node = 'A'
goal_node = 'E'
path = a_star(graph, start_node, goal_node, euclidean_distance)
if path:
print(f"Shortest path from {start_node} to {goal_node}: {path}")
else:
print(f"No path found from {start_node} to {goal_node}")
Misol izohi
A* algoritmi tekshirilishi kerak bo'lgan tugunlarni kuzatib borish uchun ustuvor navbatdan (`open_set`) foydalanadi, eng past taxminiy umumiy narxga ega bo'lganlarni (f_score) birinchi o'ringa qo'yadi. `g_score` lug'ati boshlang'ich tugundan har bir tugungacha bo'lgan narxni saqlaydi, `f_score` lug'ati esa har bir tugun orqali maqsadga taxminiy umumiy narxni saqlaydi. `came_from` lug'ati maqsad tuguniga yetib borgandan so'ng eng qisqa yo'lni qayta tiklash uchun ishlatiladi.
Murakkablik tahlili
- Vaqt murakkabligi: A* qidiruvining vaqt murakkabligi evristik funksiyaga juda bog'liq. Eng yaxshi holatda, mukammal evristika bilan A* eng qisqa yo'lni O(V + E) vaqt ichida topishi mumkin. Eng yomon holatda, yomon evristika bilan u Dijkstra algoritmiga aylanib, O((V + E) log V) vaqt murakkabligiga ega bo'lishi mumkin.
- Fazo murakkabligi: O(V), ochiq to'plamni, yopiq to'plamni, g_score, f_score va came_from lug'atlarini saqlash uchun.
Amaliy mulohazalar va optimallashtirishlar
- To'g'ri Algoritmni Tanlash: Dijkstra algoritmi odatda manfiy bo'lmagan qirrali og'irliklari bo'lgan grafiklar uchun eng tezkoridir. Bellman-Ford manfiy qirrali og'irliklar mavjud bo'lganda zarur, lekin u sekinroq. A* qidiruvi, agar yaxshi evristika mavjud bo'lsa, Dijkstra-dan ancha tezroq bo'lishi mumkin.
- Ma'lumotlar Tuzilmalari: Ustuvor navbatlar (to'plamlar) kabi samarali ma'lumotlar tuzilmalaridan foydalanish ishlashni sezilarli darajada yaxshilashi mumkin, ayniqsa katta grafiklar uchun.
- Grafikni ifodalash: Grafikni ifodalash tanlovi (qo'shnilik ro'yxati va qo'shnilik matritsasi) ham ishlashga ta'sir qilishi mumkin. Qo'shnilik ro'yxatlari ko'pincha siyrak grafiklar uchun samaraliroqdir.
- Evristik dizayn (A* uchun): Evristik funksiyaning sifati A* ishlashi uchun juda muhimdir. Yaxshi evristika qabul qilinishi kerak (hech qachon ortiqcha baholanmasligi) va iloji boricha aniq bo'lishi kerak.
- Xotiradan foydalanish: Juda katta grafiklar uchun xotiradan foydalanish tashvishga solishi mumkin. Grafikni bo'laklarga bo'lib qayta ishlash uchun iteratorlar yoki generatorlardan foydalanish kabi usullar xotira izini kamaytirishga yordam beradi.
Haqiqiy dunyo ilovalari
Eng qisqa yo'l algoritmlari haqiqiy dunyo ilovalarining keng doirasiga ega:
- GPS Navigatsiya: Masofa, trafik va yo'llarning yopilishi kabi omillarni hisobga olgan holda, ikki joy o'rtasidagi eng qisqa yo'nalishni topish. Google Maps va Waze kabi kompaniyalar ushbu algoritmlarga juda bog'liq. Masalan, Londondan Edinburgga yoki Tokiodan Osakaga mashinada eng tez yo'lni topish.
- Tarmoq marshrutlash: Ma'lumotlar paketlarining tarmoq bo'ylab harakatlanishi uchun optimal yo'lni aniqlash. Internet-provayderlar trafigi samarali yo'naltirish uchun eng qisqa yo'l algoritmlaridan foydalanadilar.
- Logistika va ta'minot zanjirini boshqarish: Yuk mashinalari yoki samolyotlar uchun yetkazib berish yo'nalishlarini optimallashtirish, masofa, narx va vaqt cheklovlari kabi omillarni hisobga olish. FedEx va UPS kabi kompaniyalar samaradorlikni oshirish uchun ushbu algoritmlardan foydalanadilar. Misol uchun, Germaniyadagi ombordan turli Evropa mamlakatlaridagi mijozlarga tovarlar uchun eng tejamli tashish yo'nalishini rejalashtirish.
- Resurslarni taqsimlash: Resurslarni (masalan, o'tkazish qobiliyati, hisoblash quvvati) foydalanuvchilar yoki vazifalarga narxni minimallashtiradigan yoki samaradorlikni maksimal darajaga yetkazadigan tarzda taqsimlash. Bulutli hisoblash provayderlari resurslarni boshqarish uchun ushbu algoritmlardan foydalanadilar.
- O'yin ishlab chiqish: Video o'yinlardagi belgilar uchun yo'l topish. A* qidiruvi o'zining samaradorligi va murakkab muhitlarni boshqara olish qobiliyati tufayli bu maqsadda keng qo'llaniladi.
- Ijtimoiy tarmoqlar: Ijtimoiy tarmoqda ikkita foydalanuvchi o'rtasidagi eng qisqa yo'lni topish, ular o'rtasidagi ajratish darajasini ifodalash. Misol uchun, Facebook yoki LinkedIn-dagi istalgan ikki kishi o'rtasidagi "olti darajali ajratish"ni hisoblash.
Ilg'or mavzular
- Ikki tomonlama qidiruv: Boshlang'ich va maqsad tugunlardan bir vaqtning o'zida qidiruv, o'rtada uchrashuv. Bu qidiruv maydonini sezilarli darajada kamaytirishi mumkin.
- Qisqarish ierarxiyalari: Juda tez eng qisqa yo'l so'rovlariga imkon beradigan tugunlar va qirralar ierarxiyasini yaratadigan oldindan ishlash texnikasi.
- ALT (A*, diqqatga sazovor joylar, uchburchak tengsizligi): Evristik baholashni yaxshilash uchun diqqatga sazovor joylar va uchburchak tengsizligidan foydalanadigan A* asosidagi algoritmlar oilasi.
- Parallel eng qisqa yo'l algoritmlari: Ayniqsa juda katta grafiklar uchun eng qisqa yo'lni hisoblashni tezlashtirish uchun bir nechta protsessor yoki oqimdan foydalanish.
Xulosa
Eng qisqa yo'l algoritmlari kompyuter fanida va undan tashqarida keng ko'lamli muammolarni hal qilish uchun kuchli vositalardir. Python, o'zining ko'p qirraliligi va keng kutubxonalari bilan ushbu algoritmlarni amalga oshirish va sinovdan o'tkazish uchun ajoyib platformani taqdim etadi. Dijkstra, Bellman-Ford va A* qidiruvi ortidagi tamoyillarni tushunish orqali siz yo'l topish, marshrutlash va optimallashtirish bilan bog'liq haqiqiy dunyo muammolarini samarali hal qila olasiz.
Grafikingizning xususiyatlariga (masalan, qirrali og'irliklar, o'lcham, zichlik) va evristik ma'lumotlarning mavjudligiga qarab, ehtiyojlaringizga eng mos keladigan algoritmni tanlashni unutmang. Ishlashni yaxshilash uchun turli xil ma'lumotlar tuzilmalari va optimallashtirish texnikalari bilan tajriba o'tkazing. Ushbu tushunchalarni yaxshi tushunish bilan siz turli xil eng qisqa yo'l muammolarini hal qilishga yaxshi tayyorlangan bo'lasiz.