Графы
8.2. Отношение достижимости на графе
Отношению достижимости на графе принадлежат пары вершин, для которых существует соединяющий их путь. Это отношение является рефлексивно-транзитивным замыканием отношения edge. Отношение достижимости на графе является отношением эквивалентности, поэтому все вершины распадаются на классы связанных между собой вершин. В следующей программе находятся вершины, достижимые из заданной вершины, а также классы достижимых друг из друга вершин.
Факт-переменная conn в программе используется, во-первых, для предотвращения зацикливания и, во-вторых, для того чтобы достижимые вершины возвращались по одному разу, без повторов.
open core, console, list class facts - graph arc: (string, string, unsigned). class facts conn : string* := []. class predicates connected: (string) -> string multi. connected1: (string) -> string multi. edge: (string, string, unsigned) nondeterm (i,o,o). components: () -> string**. components: (string*) -> string**. clauses edge(X, Y, Dist):- arc(X, Y, Dist); arc(Y, X, Dist). connected(X) = connected1(X):- conn := [X]. connected1(X) = X. connected1(X) = connected1(Y):- edge(X, Y, _), not(isMember(Y, conn)), conn := [Y | conn]. components() = components(VList):- VList = removeDuplicates([V || arc(V, _, _); arc(_, V, _)]). components([V | L]) = [Cmp | components(difference(L, Cmp))]:- Cmp = [Vertex || Vertex = connected(V)]. components([]) = []. run():- file::consult("graph.txt", graph), write(connected("Самара")), nl, fail; write("\nВершины компонент связности:\n"), forAll(components(), {(L):- write(L), nl}), _ = readLine().Пример 8.3. Отношение достижимости
Предикат removeDuplicates удаляет из списка дубликаты элементов.
Упражнение 3. Найдите вершины, не достижимые из заданной вершины.
8.3. Поиск кратчайших путей
Рассмотрим методы поиска кратчайших путей на графе.
В приведенной ниже программе для поиска кратчайших путей используется самый простой декларативный метод, в котором реализуется определение: путь — кратчайший, если не существует путей короче него2Пример приведен для версии 7.5. В версии 7.4 вместо выражения not(A in B) должно быть not(list::isMember(A, B)). Вместо A и B в листингах стоят разные переменные..
class facts - graph arc: (string, string, unsigned). class predicates shortestPath: (string, string, string* [out], unsigned [out]) nondeterm. path: (string, string, string*, string* [out], unsigned, unsigned [out]) nondeterm. shorter: (string, string, unsigned) determ. edge: (string, string [out], unsigned [out]) nondeterm. clauses edge(X, Y, Dist):- arc(X, Y, Dist); arc(Y, X, Dist). shortestPath(Start, Goal, list::reverse(Path), Dist):- path(Start, Goal, [Start], Path, 0, Dist), not(shorter(Start, Goal, Dist)). shorter(Start, Goal, Dist):- path(Start, Goal, [Start], _, 0, Dist1), Dist1 < Dist, !. path(Goal, Goal, Path, Path, Dist, Dist):- !. path(V, Goal, CurrPath, Path, CurrDist, Dist):- edge(V, NextV, D), not(NextV in CurrPath), path(NextV, Goal, [NextV | CurrPath], Path, CurrDist + D, Dist). run():- file::consult("graph.txt", graph), shortestPath("Москва", "Новосибирск", Path, D), write(string::concatWithDelimiter(Path, " -> "), " : ", D), nl, fail; _ = readLine().Пример 8.4. Кратчайшие пути и поиск в глубину
Упражнение 4. Найдите кратчайшие пути из Москвы до Новосибирска, не проходящие через Пермь.