Графтағы алгоритм түрлері
Google Maps сенің үйіңнен мектепке дейінгі ЕҢ ҚЫСҚА жолды қалай табады? Ол миллиондаған көше мен қиылысты (граф түрінде) сақтап, арнайы алгоритммен «іздейді». Өткен сабақта графты сипаттауды үйрендік, ал бүгін — графтан нақты жауап табатын алгоритмдерді: DFS, BFS және ең қысқа жол іздеу тәсілдерін қарастырамыз.
Графтағы іздеу — белгілі бір шартқа сай төбені табу үшін графтың барлық төбесін жүйелі қарап шығу. Ең кең тараған екі әдіс бар — оларды лабиринтте жүру мысалымен түсінуге болады.
Тереңнен іздеу (Depth First Search, DFS) — бір тармақты СОҢЫНА ДЕЙІН толық өтіп, содан кейін ғана келесі тармаққа өту арқылы графты зерттеу.
Берілді: 10 төбелі граф, көршілестік тізімі, бастапқы төбе s=0.
n = 10
adj_list = [[2, 4, 6], [9], [0, 3], [2, 4], [0, 3], [], [0, 7, 8], [6], [6], [1]]
s = 0
visited = [False] * n
def dfs(v):
visited[v] = True
for w in adj_list[v]:
if visited[w] == False:
dfs(w)
dfs(s)
print(visited.count(True)) Түсіндірме: dfs(v) функциясы v төбесін «қаралды» деп белгілеп, оның әр көршісі үшін, егер ол әлі қаралмаса, ӨЗІН-ӨЗІ рекурсивті шақырады — дәл осылай бір тармақ соңына дейін жүреді.
Жауабы: visited.count(True) — бастапқы төбеден жетуге болатын барлық төбенің саны.
Көлденеңінен іздеу (Breadth First Search, BFS) — алдымен бастапқы төбемен КӨРШІЛЕС барлық төбені қарайды (1 қадам қашықтықтағылар), содан кейін 2 қадам қашықтықтағыларды, т.с.с.
DFS рекурсия (немесе стек) қолданса, BFS уақытша ақпаратты сақтау үшін кезек (жаңа элемент соңына қосылып, ескісі басынан алынатын тізім) қолданады.
Берілді: 6 төбелі граф, көршілестік тізімі, бастапқы төбе s.
adj = [[1, 3], [0, 3, 4, 5], [4, 5], [0, 1, 5], [1, 2], [1, 2, 3]]
level = [-1] * len(adj)
def bfs(s):
global level
level[s] = 0
stack = [s]
while stack:
v = stack.pop(0)
for w in adj[v]:
if level[w] == -1:
level[w] = level[v] + 1
stack.append(w)
bfs(0)
print(level) Түсіндірме: stack.pop(0) кезектің БАСЫНАН алады (нағыз кезек әрекеті); level[w] == -1 болса, төбе әлі қаралмаған дегенді білдіреді, оған алдыңғы деңгейден 1 артық мән беріліп, кезекке қосылады.
Ең ҚЫСҚА жолды табу үшін ең қарапайым идея — әр қадамда ЕҢ ЖАҚЫН қалаға өту. Бірақ бұл ӘРДАЙЫМ дұрыс жауап бере ме?
Берілді: A-B(7), A-C(9), A-F(14), B-C(10), C-F(2), C-E(6), F-E(9). A-дан E-ге қысқа жол керек.
«Тойымсыз» шешім: A→B (7) → C (10) → F (2) → E (9), жалпы: 28.
Нақты ең қысқа жол: A→C (9) → F (2) → E (9), жалпы: 20.
Жауабы: тойымсыз алгоритм 28 береді, бірақ ОҢТАЙЛЫ жауап — 20! Демек, әр қадамда ЖЕРГІЛІКТІ ең жақсы таңдау ЖАЛПЫ ең жақсы нәтижеге әкелмеуі мүмкін.
Информатикада ең қысқа жол іздеудің бірнеше атақты алгоритмі бар — олардың айырмашылығы, НЕ ІЗДЕЙТІНІНДЕ:
| Алгоритм | Не табады |
|---|---|
| Прима-Крускал | Барлық төбені байланыстыратын МИНИМАЛДЫ ЖАЛПЫ ұзындықтағы ағаш (N қаланы ең аз кабельмен байланыстыру) |
| Дейкстра | БІР бастапқы төбеден БІР соңғы төбеге дейінгі ең қысқа жол |
| Флойд | БАРЛЫҚ төбе жұптары арасындағы ең қысқа жол (бір реттен) |
Прима-Крускал — тойымсыз алгоритм, БІРАҚ бұл жерде ол әрқашан дұрыс жұмыс істейді: әр қадамда ағашқа қосылмаған ЕҢ АРЗАН қабырға таңдалады, циклдің құрылмауын бақылау үшін әр төбе «түспен» белгіленеді.
col = [i for i in range(N)]
ostov = []
for k in range(N - 1):
minDist = 1e10
for i in range(N):
for j in range(N):
if col[i] != col[j] and W[i][j] < minDist:
iMin, jMin, minDist = i, j, W[i][j]
ostov.append((iMin, jMin))
c = col[jMin]
for i in range(N):
if col[i] == c:
col[i] = col[iMin] Флойд алгоритмі ерекше ықшам — небәрі үш кірістірілген цикл арқылы БАРЛЫҚ жұптың ең қысқа жолын табады, әр аралық k төбе арқылы өту қысқарақ бола ма деп тексереді:
for k in range(N):
for i in range(N):
for j in range(N):
if a[i][k] + a[k][j] < a[i][j]:
a[i][j] = a[i][k] + a[k][j] Дейкстра алгоритмі бір бастапқы төбеден бастап, әр қадамда ТЕКСЕРІЛМЕГЕН, ЕҢ ЖАҚЫН төбені таңдап, оның арқылы басқа қашықтықтарды «қысқартады»:
D[K] = 0 # бастапқы төбе қашықтығы 0
for i in range(N):
M = -1
for j in range(N):
if not Use[j] and (M == -1 or D[j] < D[M]):
M = j
Use[M] = True
for j in range(N):
if D[j] > A[M][j] + D[M]:
D[j] = A[M][j] + D[M] - DFS — бір тармақты соңына дейін өтіп зерттейді (рекурсия/стек); BFS — деңгей-деңгей зерттейді (кезек).
- «Тойымсыз» алгоритм әр қадамда жергілікті ең жақсыны таңдайды, бірақ жалпы оптимал болмауы мүмкін.
- Прима-Крускал — минималды ішкі ағаш; Дейкстра — бір жұп арасындағы қысқа жол; Флойд — барлық жұп арасындағы қысқа жол.