Приведите пример на Prolog, где простой набор фактов и правил выдаёт неожиданные решения из‑за порядка поиска (depth‑first), объясните почему это происходит и как перестроить программу или запросы, чтобы получить все корректные решения

25 Сен в 13:28
21 +1
0
Ответы
1

Ниже — простой и наглядный пример, показывающий, как порядок поиска Prolog глубинныйобход/depth‑firstSLDглубинный обход / depth‑first SLDглубинныйобход/depthfirstSLD может привести к «неожиданному» поведению зависанию/пропускурешенийзависанию / пропуску решенийзависанию/пропускурешений.

1) Проблемный леворекурсивныйлеворекурсивныйлеворекурсивный вариант

Код:
edgea,ba,ba,b.
edgeb,cb,cb,c.
edgec,dc,dc,d.

% леворекурсивное определение транзитивного замыкания
reachableX,YX,YX,Y :-
reachableX,ZX,ZX,Z,
edgeZ,YZ,YZ,Y.
reachableX,YX,YX,Y :-
edgeX,YX,YX,Y.

Запрос:
?- reachablea,da,da,d.

Ожидаем: true (путь a->b->c->d). Фактически — Prolog входит в бесконечную рекурсию и «зависает» недоходитдовторогоправилаиневозвращаетответне доходит до второго правила и не возвращает ответнедоходитдовторогоправилаиневозвращаетответ. Почему?

Объяснение:

Prolog делает поиск в глубину и пробует первое правило reachable/2 первым.Первое правило вызывает reachableX,ZX,ZX,Z — рекурсивный вызов с теми же «размерными» аргументами, до тех пор, пока поиск не исчерпает стек илизациклитсяили зациклитсяилизациклится. Оно не «съедает» ребро edgeedgeedge до тех пор, пока внутренняя рекурсия не вернёт управление.Из‑за этого база второеправилоedge(X,Y)второе правило edge(X,Y)второеправилоedge(X,Y) проверяется только после того, как все вложенные рекурсивные вызовы вернутся — а вложенные вызовы сами снова пытают первое правило и т. д. => бесконечная рекурсия.

2) Как перестроить программу, чтобы получить все корректные решения

Есть несколько способов.

A) Поменять порядок правил правилосбазой—первымправило с базой — первымправилосбазойпервым. Правильный праворекурсивный вариант:
edgea,ba,ba,b.
edgeb,cb,cb,c.
edgec,dc,dc,d.

reachableX,YX,YX,Y :- edgeX,YX,YX,Y.
reachableX,YX,YX,Y :- edgeX,ZX,ZX,Z, reachableZ,YZ,YZ,Y.

Теперь ?- reachablea,da,da,d. вернёт true, и ?- findallY,reachable(a,Y),LY, reachable(a,Y), LY,reachable(a,Y),L. вернёт b,c,db,c,db,c,d дляациклическогографаработакорректнадля ациклического графа работа корректнадляациклическогографаработакорректна.

B) Защита от циклов — передавать список посещённых вершин
Это нужно, если в графе есть циклы: без проверки member цикл приведёт к бесконечности даже в праворекурсивной версии.

edgea,ba,ba,b. edgeb,ab,ab,a. edgeb,cb,cb,c.

reachableX,YX,YX,Y :- reachableX,Y,[X]X,Y,[X]X,Y,[X].
reachable(X,Y,_) :- edgeX,YX,YX,Y.
reachableX,Y,VisitedX,Y,VisitedX,Y,Visited :-
edgeX,ZX,ZX,Z,
+ memberZ,VisitedZ,VisitedZ,Visited,
reachableZ,Y,[Z∣Visited]Z,Y,[Z|Visited]Z,Y,[ZVisited].

Теперь запросы безопасно найдут все достижимые вершины и не зациклются.

C) Таблирование еслиреализацияPrologподдерживает—SWI,XSBит.д.если реализация Prolog поддерживает — SWI, XSB и т.д.еслиреализацияPrologподдерживаетSWI,XSBит.д. Таблирование memoizationmemoizationmemoization устраняет проблему бесконечной рекурсии для многих леворекурсивных определений:

:- table reachable/2.
reachableX,YX,YX,Y :- edgeX,YX,YX,Y.
reachableX,YX,YX,Y :- edgeX,ZX,ZX,Z, reachableZ,YZ,YZ,Y.

D) Альтернативные стратегии поиска
Если вам нужен гарантированный «короткий путь» или поведение, отличное от глубинного обхода, используйте реализацию поиска в ширину / итеративное углубление / явный BFS реализоватьотдельнойпроцедуройреализовать отдельной процедуройреализоватьотдельнойпроцедурой.

3) Замечания про запросы на все решения

Использование findall/3, bagof/3 и т.п. вернёт все решения, но не спасёт от бесконечной рекурсии: если программа не завершает поиск т.е.застреваетт. е. застреваетт.е.застревает, findall тоже «повиснет».Поэтому важно исправить определение поменятьпорядок/добавитьvisited/включитьтаблирование/использоватьдругойалгоритмпоискапоменять порядок / добавить visited / включить таблирование / использовать другой алгоритм поискапоменятьпорядок/добавитьvisited/включитьтаблирование/использоватьдругойалгоритмпоиска.

Короткое резюме:

Причина: Prolog использует depth‑first SLD-резолюцию, и леворекурсивные правила могут вызывать бесконечную рекурсию до рассмотрения базовых случаев.Решения: поменять порядок правил базапреждерекурсиибаза прежде рекурсиибазапреждерекурсии, добавить контроль посещённых вершин, включить таблирование или применить другой алгоритм поиска BFS/итеративноеуглублениеBFS / итеративное углублениеBFS/итеративноеуглубление.

Если хотите, могу привести полностью рабочие примеры кода дляSWI‑Prologдля SWI‑PrologдляSWIProlog для каждого из подходов и показать поведение на конкретных запросах.

25 Сен в 14:04
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир