Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Искусственный_интеллект.doc
Скачиваний:
20
Добавлен:
13.09.2019
Размер:
786.43 Кб
Скачать

1.2. Поиск в ширину в пространстве состояний. Поиск путей-кандидатов

1.2.1. Рекурсивная процедура поиска в ширину

Поиск в глубину хорошо работает в таких пространствах состояний, где и тупики, и решения удалены примерно на одно и то же расстояние от начала поиска. Наиболее плохой случай для поиска в глубину следующий: какая-то ветвь графа состояний очень протяженная или бесконечная, а решения находятся на других ветках, на небольшом расстоянии от начала поиска.

Для таких пространств более эффективен поиск в ширину. Если в глубине на каждом шаге имеется ровно один путь-кандидат в решение, то в ширине таких путей-кандидатов несколько.

Рассмотрим поиск в ширину на модельном графе (рис. 2).

1

5

Р

3

2

4

исунок 2. Модельный граф для поиска в ширину

Начнем поиск в ширину из вершины 1 — имеем начальный путь [1]. Просмотрели вершины 2, 3, 4 смежные с вершиной 1 — получили пути-кандидаты [[2, 1], [3, 1], [4, 1]]. Каждый из них в свою очередь может дать несколько продолжений и т.д.

Будем на каждом шаге хранить список путей-кандидатов. Новой будет считаться любая вершина, не содержащаяся в текущем пути, поэтому, добавив к главной цели fail, можно сгенерировать все пути в порядке возрастания их длин.

Первым аргументом процедуры width будет текущий список путей-кандидатов, вторым — окончательный путь-решение. Каждый новый путь становится головой списка путей кандидатов.

width(Ways,Sol), где Ways — список путей, Sol — решение.

Граничное условие рекурсии

width([[Z|Was]|T],[Z|Was]):-

end(Z).

[Z|Was] — путь-решение, который окончился целевой вершиной Z.

width([[X|Was]|T],Sol):-

findall(Way,continue(X,Was,Way),LC),

conc(T,LC,T1),!,

width(T1,Sol).

Findall собирает в список LC все продолжения пути [X|Was] на один шаг, и ширина вызывается с новым списком путей T1.

Продолжение пути на один шаг производится в процедуре continue.

continue(X,T,[Y,X|T]):-

go(X,Y),

not(member(Y,T)).

Если вершина X тупиковая, то findall вернет список LC=[]. Путь [X|Was] будет удален из списка путей-кандидатов и произойдет вызов width с T1=T.

Если все пути-кандидаты тупиковые и не содержат целевых вершин, то width будет вызвана с пустым списком и вернет неудачу. Так как отсечение блокирует возвраты, то неудачу вернет и самый первый вызов width из solve.

1.2.2. Множества начальных вершин, прямой и обратный поиск

Пусть начальное состояние не единственное. При поиске в глубину будем последовательно вызывать процедуру из каждого начального состояния:

solve([H|T]):-

depth(H,Sol).

solve([_|T]):-

solve(T).

При поиске в ширину образуем из начальных вершин начальные пути и соберем их в список: L0 — список начальных вершин, W0 — список начальных путей.

solve(L0):-

findall(Was,make_way(X,L0,Way),W0),

width(W0,Sol).

make_way(X,L0,[X]):-

member(X,L0).

Если начальных состояний довольно много, а конечное одно, удобно производить поиск обратный поиск — от конечного к начальному. Если ходы в пространстве не обратимы, то вместо процедуры go может потребоваться обратная процедура go1. Кроме того, поиск в ширину можно сделать двунаправленным — для просмотра меньшей области пространства.

Рисунок 3. Обратный и двунаправленный поиск в ширину

Задание. Написать программу составления упорядоченного плана перестановок кубиков за минимальное число шагов с помощью поиска в ширину в пространстве состояний. Определить ее вычислительную сложность, то есть, как быстро будет расти число шагов, производимых алгоритмом, при неограниченном увеличении размерности пространства состояний.