Изкуствен интелект Упражнение № 2
DESCRIPTION
Изкуствен интелект Упражнение № 2. спец. Информатика, IV курс 2005/2006 учебна година. Задачи за търсене в пространството от състояния. Удовлетворяване на ограничения ( Constraint satisfaction problem - CSP) Т ърсене на целево състояние Моделиране на игри за двама играчи. - PowerPoint PPT PresentationTRANSCRIPT
Изкуствен интелектИзкуствен интелект
Упражнение № 2
спец. Информатика, IV курс
2005/2006 учебна година
Задачи за търсене в Задачи за търсене в пространството от пространството от
състояниясъстоянияУдовлетворяване на ограничения
((Constraint satisfaction problem - Constraint satisfaction problem - CSP)CSP)
Търсене на целево състояниеМоделиране на игри за двама играчи
ТТърсене на целево ърсене на целево състояниесъстояние
( (Търсене на път в графТърсене на път в граф)) ГрафътГрафът (graph) (graph) се състои от множество NN от възли възли // върхове / състояния ( върхове / състояния (nodes)nodes) и множество AA от наредени двойки от върхове, наречени дъги (дъги (arcsarcs)) .
Върхът nn22 е съсед (съсед (neighbor)neighbor) на n1n1, ако съществува дъга между nn11 и nn22, т.е. <n<n11,n,n22> є A.> є A.
ПътПът (path) (path) е последователност от върхове nn00, , nn11, ..., ... nnk k ¸ такава че <n<ni-i-
11,n,nii> є A, i=1..k .> є A, i=1..k .
ТТърсене на целево ърсене на целево състояниесъстояние
( (Търсене на път в графТърсене на път в граф)) Дадени са начални върхове/състоянияначални върхове/състояния
(start nodes)(start nodes) и крайни/ целеви крайни/ целеви състояния (състояния (goal nodesgoal nodes)) , решение решение ((solutionsolution)) е път от начално състояние до крайно състояние.
Пример: ГрафПример: Граф
Пример: МрежаПример: Мрежа
s
g
Общ алгоритъмОбщ алгоритъм
Общ алгоритъм за търсене: даден е граф начални състояния целеви състояния последователно да се изследват
пътищата от началните състояния
Общ алгоритъмОбщ алгоритъмПоддържа се фронт/границафронт/граница ((frontierfrontier))
от пътищата, които са били изследвани
По време на процеса на търсене фронта се разширява в посока към неизследваните възли, докато се достигне до целеви възел
Общ алгоритъмОбщ алгоритъм
Предполагаме, че след като алгоритъма за търсене намери един път, може да му бъде зададено да търси още решения и тогава процесът трябва да продължи
Начинът, по който фронта се разширява и това точно коя стойност от фронта се избира дефинира стратегията на стратегията на търсене (търсене (search strategysearch strategy))..
start node
explored nodes
unexplored nodes
frontier
Основни алгоритмиОсновни алгоритми
Неинформирано (сляпо) търсене
Информирано (евристично) търсене
Основни алгоритмиОсновни алгоритми
Неинформирано (сляпо) търсене търсене в дълбочина ((Depth-first search)Depth-first search)
търсене в ширина ((Breadth-first search)Breadth-first search)
търсене в ограничена дълбочина ((Depth-bound search)Depth-bound search)
итеративно търсене по нива((Iterative-deepening search)Iterative-deepening search)
Търсене в дълбочинаТърсене в дълбочина
При търсенето в дълбочина фронтът се обработва като стек
Ако фронта е [p1,p2, . . .][p1,p2, . . .] избира се p1p1
пътищата pp11', ', pp11'',…, p'',…, p11(k)(k), които разширяват
p1p1 се добавят в началото на стека (преди p2p2), т.е.
[p[p11', ', pp11'',…, p'',…, p11(k)(k), p2, . . .]p2, . . .]
p2p2 се обработва едва след като всички пътища, които са продължение на p1p1 са били изследвани
Представяне на графаПредставяне на графа
arc(Node1,Node2).arc(Node1,Node2).
arc(Node1,Node2,Cost).arc(Node1,Node2,Cost).
arc(a,b). arc(d,e).arc(a,b). arc(d,e).
arc(a,d). arc(e,c).arc(a,d). arc(e,c).
arc(b,a).arc(b,a). arc(e,d).arc(e,d).
arc(b,c).arc(b,c).
arc(c,e).arc(c,e).
arc(d,a).arc(d,a).
arc(d,b).arc(d,b).
arc(d,c).arc(d,c).
a
e
d
c
b
Построяване на Построяване на дървотодървото
arc(a,b).arc(a,b).
arc(a,d).arc(a,d).
arc(b,a).arc(b,a).
arc(b,c).arc(b,c).
arc(c,e).arc(c,e).
arc(d,a).arc(d,a).
arc(d,b).arc(d,b).
arc(d,c).arc(d,c).
arc(d,e).arc(d,e).
arc(e,c).arc(e,c).
arc(e,d).arc(e,d).
a
e
d
c
b
a
b d
c
e
b c
ec
ed
e
c
Представяне на фронта Представяне на фронта припри
Търсене в дълбочинаТърсене в дълбочина[[a]][[a]][[b a] [d a]][[b a] [d a]][[c b a] [d a]][[c b a] [d a]][[e c b a] [d a]][[e c b a] [d a]]I-во решение: I-во решение: [e c b a] => [a b c e][e c b a] => [a b c e]Преудовлетворяване:Преудовлетворяване:[[d a]][[d a]][[b d a] [c d a] [e d a]][[b d a] [c d a] [e d a]][[c b d a] [c d a] [e d a]][[c b d a] [c d a] [e d a]][[e c b d a] [c d a] [e d a]][[e c b d a] [c d a] [e d a]]II-ро решение: II-ро решение: [e c b d a] => [a d b c e][e c b d a] => [a d b c e]Преудовлетворяване:Преудовлетворяване:[[c d a] [e d a]][[c d a] [e d a]][[e c d a] [e d a]][[e c d a] [e d a]]III-то решение: III-то решение: [e c d a] => [a d c e][e c d a] => [a d c e]Преудовлетворяване:Преудовлетворяване:[[e d a]][[e d a]]IV-то решение: IV-то решение: [e d a] => [a d e][e d a] => [a d e]
a
b d
c
e
b c
ec
ed
e
c
Начално състояние
Целево състояние
Търсене в дълбочинаТърсене в дълбочина1
133
2
124 14
75
108
119
6
1615
Търсене в дълбочина Търсене в дълбочина ((Depth-first)Depth-first)
depth_first([Path|depth_first([Path|StackStack],Goal,FinalPath) :-],Goal,FinalPath) :-
extend(Path,NewPaths), extend(Path,NewPaths),
append(NewPaths, append(NewPaths, StackStack,, NewNewStackStack),),
depth_first(Newdepth_first(NewStackStack,Goal,FinalPath).,Goal,FinalPath).
Търсене в дълбочина Търсене в дълбочина ((Depth-first)Depth-first)
depth_first([[Goal|Path]|_],Goal,depth_first([[Goal|Path]|_],Goal,FinalFinalPath)Path):-:-
reverse(reverse([Goal|Path][Goal|Path],FinalPath,FinalPath).).
reverse([],[]).reverse([],[]).
reverse([X|Rest],Lireverse([X|Rest],Lisst):-t):-
reverse(Rest,NewRest),reverse(Rest,NewRest),
append(NewRest,[X],List).append(NewRest,[X],List).
Генериране на всички Генериране на всички решениярешения
Често при решаване на задачи се налага да се генерират всички решения на цел и да се съхраняват в единична структура от данни
Това позволява да се преобразува търсенето с връщане назад (backtracking) в итерация и решава един сериозен проблем: при backtracking е много трудно да се предава информация от една “итерация” на backtracking към следваща
Стандартни предикатиСтандартни предикати
Съществуват три “стандартни” предиката за генериране на всички решения findall/3findall/3 събира всички екземпляри в
реда, в който те са намерени, но не обработва свободни променливи вдясно
setof/3setof/3 връща множество от екземпляри (списък в стандартния ред без повторения) и обработва свободни променливи вдясно
bagof/3bagof/3 хибрид между findall/3findall/3 и setof/3setof/3
Стандартни предикатиСтандартни предикати
findall(Template,Enumerator,List)
Намира всички екземпляри на Template, за които Enumerator е удовлетворим, и ги натрупва в списък по реда, в който те са намерени (т.е. първият намерен екземпляр се поставя като първи елемент на списъка, а последният - като последен елемент на списъка) и свързва List с този списък. Обикновено List е променлива, а Template е променлива или терм, чийто аргументи са променливи, но могат да бъдат и произволни
Пример 1Пример 1
likes(bill, cider).likes(bill, cider).
likes(dick, beer).likes(dick, beer).
likes(harry, beer).likes(harry, beer).
likes(jan, cider).likes(jan, cider).
likes(tom, beer).likes(tom, beer).
likes(tom, cider).likes(tom, cider).
| ?- findall(X,likes(X,Y),S).| ?- findall(X,likes(X,Y),S).
S = [bill,dick,harry,jan,tom,tom]S = [bill,dick,harry,jan,tom,tom]
Пример 1Пример 1likes(bill, cider).likes(bill, cider).likes(dick, beer).likes(dick, beer).likes(harry, beer).likes(harry, beer).likes(jan, cider).likes(jan, cider).likes(tom, beer).likes(tom, beer).likes(tom, cider).likes(tom, cider).
| ?- findall(X| ?- findall(X/Y/Y,likes(X,Y),S).,likes(X,Y),S).S = [billS = [bill/cider/cider,, dickdick/beer/beer,, harryharry/beer/beer,, janjan/cider/cider,,
tomtom/beer/beer,, tomtom/cider/cider]]| ?- findall(X,likes(X,| ?- findall(X,likes(X,beerbeer),S).),S).S = [dick,S = [dick, harry,harry, tom]tom]| ?- findall(| ?- findall(YY,likes(,likes(tomtom,,YY),S).),S).S = [S = [beer,ciderbeer,cider]]
Разширяване на Разширяване на фронтафронта
extend([Node|Path],NewPaths) :-extend([Node|Path],NewPaths) :-
findall([NewNode,Node|Path],findall([NewNode,Node|Path],
(arc(Node,NewNode,_), (arc(Node,NewNode,_),
notnot member(NewNode, member(NewNode,[Node|[Node|PathPath]])), )),
NewPaths).NewPaths).
Търсене в дълбочина Търсене в дълбочина ((Depth-first)Depth-first)
depth_first([[Goal|Path]|_],Goal,FinalPath):-
reverse([Goal|Path],FinalPath).
depth_first([Path|Stack],Goal,FinalPath) :-
extend(Path,NewPaths),
append(NewPaths,Stack,NewStack),
depth_first(NewStack,Goal,FinalPath).
extend([Node|Path],NewPaths) :-
findall([NewNode,Node|Path],
(arc(Node,NewNode,_),
not member(NewNode,[Node|Path])),
NewPaths).
ПримерПримерarc('Arad','Zerind',75).arc('Arad','Sibiu',140).arc('Arad','Timisoara',118).arc('Bucharest','Fagaras',211).arc('Bucharest','Pitesti',101).arc('Bucharest','Giurgiu',90).arc('Bucharest','Urziceni',85).arc('Craiova','Dobreta',120).arc('Craiova','Rimnicu',146).arc('Craiova','Pitesti',138).arc('Dobreta','Mehadia',75).arc('Dobreta','Craiova',120).arc('Eforie','Hirsova',86).arc('Fagaras','Sibiu',99).arc('Fagaras','Bucharest',211).arc('Giurgiu','Bucharest',90).arc('Hirsova','Urziceni',98).arc('Hirsova','Eforie',86).arc('Iasi','Neamt',87).arc('Iasi','Vaslui',92).arc('Lugoj','Timisoara',111).arc('Lugoj','Mehadia',70).arc('Mehadia','Lugoj',70).
arc('Mehadia','Dobreta',75).arc('Neamt','Iasi',87).arc('Oradea','Zerind',71).arc('Oradea','Sibiu',151).arc('Pitesti','Rimnicu',97).arc('Pitesti','Craiova',138).arc('Pitesti','Bucharest',101).arc('Rimnicu','Sibiu',80).arc('Rimnicu','Pitesti',97).arc('Rimnicu','Craiova',146).arc('Sibiu','Arad',140).arc('Sibiu','Oradea',151).arc('Sibiu','Fagaras',99).arc('Sibiu','Rimnicu',80).arc('Timisoara','Arad',118).arc('Timisoara','Lugoj',111).arc('Urziceni','Bucharest',85).arc('Urziceni','Hirsova',98).arc('Urziceni','Vaslui',142).arc('Vaslui','Iasi',92).arc('Vaslui','Urziceni',142).arc('Zerind','Arad',75).arc('Zerind','Oradea',71).
ПримерПример
| ?- depth_first([['Arad']],'Craiova',Path).| ?- depth_first([['Arad']],'Craiova',Path).
Path = ['Arad','Zerind','Oradea','Sibiu',Path = ['Arad','Zerind','Oradea','Sibiu','Fagaras','Bucharest','Pitesti','Fagaras','Bucharest','Pitesti','Rimnicu','Craiova']?;'Rimnicu','Craiova']?;
Path= ['Arad','Zerind','Oradea','Sibiu',Path= ['Arad','Zerind','Oradea','Sibiu', 'Fagaras','Bucharest', 'Fagaras','Bucharest',
'Pitesti','Craiova'] 'Pitesti','Craiova']
Търсене в ширинаТърсене в ширина
При търсенето в ширина фронтът се обработва като опашка
Ако фронта е [p1,p2, . . .pn][p1,p2, . . .pn] избира се p1p1 пътищата pp11', ', pp11'',…, p'',…, p11
(k)(k) , които разширяват p1p1 се добавят в края на опашката (след pnpn), т.е. [p2, …,pn[p2, …,pn, , pp11', ', pp11'',…,p'',…,p11
(k)(k)]] и се обработват едва след като всички пътища p2, …, pnp2, …, pn, се изследват
Намира най-краткия път
Търсене в ширинаТърсене в ширина1
54
32
98 1110
1615
252423
1817
76
1312 14
2019 2221
Търсене в дълбочина Търсене в дълбочина ((Depth-first)Depth-first)
depth_first([Path|depth_first([Path|StackStack],Goal,FinalPath) :-],Goal,FinalPath) :-
extend(Path,NewPaths), extend(Path,NewPaths),
append(NewPaths, append(NewPaths, StackStack,, NewNewStackStack),),
depth_first(Newdepth_first(NewStackStack,Goal,FinalPath).,Goal,FinalPath).
Търсене в Търсене в ширинаширина ((BreadthBreadth-first)-first)
bbreadreadthth_first([Path|_first([Path|StackStack],Goal,FinalPath):-],Goal,FinalPath):-
extend(Path,NewPaths), extend(Path,NewPaths),
append(NewPaths, append(NewPaths, StackStack,, NewNewStackStack),),
bbreadreadthth_first(New_first(NewStackStack,Goal,FinalPath).,Goal,FinalPath).
Търсене в Търсене в ширинаширина ((BreadthBreadth-first)-first)
bbreadreadthth_first([Path|_first([Path|QueueQueue],Goal,FinalPath):-],Goal,FinalPath):-
extend(Path,NewPaths), extend(Path,NewPaths),
append(NewPaths, append(NewPaths, QueueQueue,, NewNewQueueQueue),),
bbreadreadthth_first(_first(NewNewQueueQueue,Goal,FinalPath).,Goal,FinalPath).
Търсене в Търсене в ширинаширина ((BreadthBreadth-first)-first)
bbreadreadthth_first([Path|_first([Path|QueueQueue],Goal,FinalPath):-],Goal,FinalPath):-
extend(Path,NewPaths), extend(Path,NewPaths),
append(append(Queue,Queue, NewPaths, NewPaths, NewNewQueueQueue),),
bbreadreadthth_first(_first(NewNewQueueQueue,Goal,FinalPath).,Goal,FinalPath).
Търсене в ширина Търсене в ширина ((Breadth-first)Breadth-first)
breadth_first([[Goal|Path]|_],Goal, FinalPath):-
reverse([Goal|Path],FinalPath).
breadth_first([Path|Queue],Goal,FinalPath) :-
extend(Path,NewPaths),
append(Queue,NewPaths,NewQueue),
breadth_first(NewQueue,Goal,FinalPath).
extend([Node|Path],NewPaths) :-
findall([NewNode,Node|Path],
(arc(Node,NewNode,_),
not member(NewNode,[Node|Path])),
NewPaths).
ПримерПример
?- breadth_first([['Arad']],'Craiova',Path).?- breadth_first([['Arad']],'Craiova',Path).
Path =['Arad','Sibiu','Rimnicu','Craiova']Path =['Arad','Sibiu','Rimnicu','Craiova']
Търсене в ограничена Търсене в ограничена дълбочинадълбочина
Задава се максимална дълбочина DepthDepth, на която ще търсим решение
Търсим път, който минава през не повече от DepthDepth на брой върха на графа
Търсене в ограничена Търсене в ограничена дълбочинадълбочина
1
63
92
54 87
1310
1211 14
Depth=4Depth=4
Търсене в дълбочина Търсене в дълбочина ((Depth-first)Depth-first)
depth_first([Path|depth_first([Path|StackStack],Goal,FinalPath) :-],Goal,FinalPath) :-
extend(Path,NewPaths), extend(Path,NewPaths),
append(NewPaths, append(NewPaths, StackStack,, NewNewStackStack),),
depth_first(Newdepth_first(NewStackStack,Goal,FinalPath).,Goal,FinalPath).
Търсене в Търсене в ограниченаограничена дълбочина (дълбочина (Depth-Depth-
boundbound))depth_depth_boundbound((DepthDepth,[,[Path|Path|StackStack],Goal,FinalPat):-],Goal,FinalPat):-
extend(Path,NewPaths), extend(Path,NewPaths),
append(NewPaths, append(NewPaths, StackStack,, NewNewStackStack),),
depth_depth_boundbound((DepthDepth,,NewNewStackStack,Goal,FinalPath).,Goal,FinalPath).
Търсене в Търсене в ограниченаограничена дълбочина (дълбочина (Depth-Depth-
boundbound))depth_depth_boundbound((DepthDepth,[,[Path|Path|StackStack],Goal,FinalPat):-],Goal,FinalPat):-
extendextend11((DepthDepth,,Path,NewPaths), Path,NewPaths),
append(NewPaths, append(NewPaths, StackStack,, NewNewStackStack),),
depth_depth_boundbound((DepthDepth,,NewNewStackStack,Goal,FinalPath).,Goal,FinalPath).
Търсене в ограничена Търсене в ограничена дълбочина (дълбочина (Depth-Depth-
bound)bound)
extend1(Depth,Path,NewPaths) :-
length(Path,Len),
Len < Depth, !,
extend(Path,NewPaths).
extend1(_,_,[]).
extend([Node|Path],NewPaths) :-
findall([NewNode,Node|Path],
(arc(Node,NewNode,_),
not member(NewNode, [Node|Path])),
NewPaths).
Търсене в ограничена Търсене в ограничена дълбочина (дълбочина (Depth-Depth-
bound)bound)depth_bound(Depth,[[Goal|Path]|_],Goal,FinalPath):-
reverse([Goal|Path],FinalPath).
depth_bound(Depth,[Path|Stack],Goal,FinalPath):-
extend1(Depth,Path,NewPaths),
append(NewPaths,Stack,NewStack),
depth_bound(Depth,NewStack,Goal,FinalPath).
extend1(Depth,Path,NewPaths) :-
length(Path,Len),
Len < Depth-1, !,
extend(Path,NewPaths).
extend1(_,_,[]).
ПримерПример
|?- depth_bound(5,[['Arad']],'Craiova',Path).
Path=['Arad','Sibiu','Rimnicu','Pitesti','Craiova'] ?;
Path=['Arad','Sibiu','Rimnicu','Craiova'] ?;
no
Итеративно търсене Итеративно търсене по нивапо нива Често пространството от състояния е много голямо
и се изискват много ресурси (време и памет), за да бъде поддържан големия фронт, което прави процеса на търсене много бавен и труден.
Основната идея е да се преизчисляват елементите от фронта, вместо да се пазят
Прави се търсене в ограничена дълбочина при Depth=1, 2, …Depth=1, 2, …
Ако целевият път не се намери на дълбочина dd се търси на дълбочина d+1d+1.
Намира най-краткия път
Итеративно търсене Итеративно търсене по нивапо нива Ако не съществува път от началното до целевото
състояние в дадения граф и вече е построено е изследвано цялото пространство от състоянията, тогава всяко нарастване на дълбочината DepthDepth ще води до построяване на едно и също пространство и многократното му претърсване.
За да се гарантира завършване на процеса, той се ограничава, като нарастването на дълбочината DepthDepth се ограничава до предварително зададена максимална стойност MaxMax, т.е. Depth Depth ≥≥ Max. Max.
Итеративно търсене Итеративно търсене по нивапо нива
1
1
32
52
43
Depth=1Depth=11
63
92
54 87
1310
1211 14 15
Depth=4Depth=41
Depth=2Depth=2
Depth=3Depth=3
76
Итеративно търсене Итеративно търсене по нива по нива (I(Iterativeterative
deepeningdeepening)) Тук като груба оценка за максималната дълбочина е използван броя на дъгите в графа.
Търсенето започва при Depth=1Depth=1
iterative_deepening(Stack,Goal,FinalPath) :-
findall(arc(X,Y,Z),arc(X,Y,Z),Graph),
length(Graph,Max),iterative_deepening1(1,Stack,Goal,FinalPath,Max).
Итеративно търсене Итеративно търсене по нива по нива (I(Iterativeterative
deepeningdeepening))iterative_deepening1(Depth,Stack,Goal,Path,Max):-
nl,write(depth=Depth),
depth_bound(Depth,Stack,Goal,Path).
iterative_deepening1(Depth,Stack,Goal,Path,Max):-
Depth1 is Depth+1,
Depth =< Max,!, iterative_deepening1(Depth1,Stack,Goal,Path,Max).
Итеративно търсене Итеративно търсене по нива по нива (I(Iterativeterative
deepeningdeepening))iterative_deepening(Stack,Goal,Path) :-
findall(arc(X,Y,Z),arc(X,Y,Z),Graph), length(Graph,N), iterative_deepening1(1,Stack,Goal,Path,N).
iterative_deepening1(Depth,Stack,Goal,Path,Max) :-
nl,write(depth=Depth),
depth_bound(Depth, Stack,Goal,Path).
iterative_deepening1(Depth,Stack,Goal,Path,Max) :-
Depth1 is Depth+1, Depth =< Max,!, iterative_deepening1(Depth1,Stack,Goal,Path,Max).
ПримерПример|?- iterative_deepening([['Arad']],'Craiova',Path).
depth=1
depth=2
depth=3
depth=4
Path = ['Arad','Sibiu','Rimnicu','Craiova'] ?