background image

Rachunek predykatów, k

 

Programowanie  w  prologu  polega  na  zapisywaniu  stwierdzeń  uŜywając  predykatów 

pierwszego rzędu. Wyniki powstają jako rezultat (automatycznego wnioskowania). 

Rachunek predykatów pierwszego rzędu:

• 

Term to funktor (symbol stanowiący nazwę relacji) z listą parametrów atomowych, np. 
lubi(jan, maria).  

• 

ZauwaŜmy, Ŝe stała jest szczególnym przypadkiem termu (bezparametrowym). 

• 

Stwierdzenie  to  jeden  lub  więcej  termów  połączonych  spójnikami  (negacja 
alternatywa  , koniunkcja 

• 

W  stwierdzeniach  mogą  pojawiać  się 
(uniwersalny  , egzystencjalny 

Klauzule  

• 

W Prologu nie uŜywa się kwantyfikatorów; w zamian za to stwierdzenia zapisuje się 
w postaci klauzul.  

• 

Ogólna postać klauzuli jest następująca: 

     

gdzie 

 są termami.  

1. 

to poprzednik klauzuli, zaś 

2. 

KaŜde stwierdzenie moŜna zapisać w formie klauzuli. Istnieje algorytm, który to robi. 

3. 

W Prologu uŜywa się postaci jeszcze 
w następniku mają zero lub jeden term (

4. 

Klauzula Horna to klauzula, w której co najwyŜej jeden element jest 
Przykładem  takiej  klauzuli  jest  {p,¬r,¬q
w postaci implikacyjnej:  p 

5. 

Klauzule z termem w następniku („z głową”) wyraŜają zaleŜności (dla 
fakty (dla 

= 0).  

6. 

Klauzule z pustym następnikiem („bez
czyli tego, co ma zostać udowodnione.

 

Rezolucja  

• 

Jest to metoda wnioskowania, którą moŜna stosować do opisanych tu stwierdzeń. 

• 

Podstawowa reguła jest następująca: wiedząc Ŝe P 

S, o ile tylko Q i R dają się zunifikować. 

• 

Z  technicznego  punktu  widzenia,  osiąga  się  to  przez  wyliczenie  Q 
i usunięcie termów, które występują po obydwu stronach T. 

Rachunek predykatów, klauzule 

Programowanie  w  prologu  polega  na  zapisywaniu  stwierdzeń  uŜywając  predykatów 

pierwszego rzędu. Wyniki powstają jako rezultat (automatycznego wnioskowania). 

Rachunek predykatów pierwszego rzędu: 

to funktor (symbol stanowiący nazwę relacji) z listą parametrów atomowych, np. 

ZauwaŜmy, Ŝe stała jest szczególnym przypadkiem termu (bezparametrowym). 

to  jeden  lub  więcej  termów  połączonych  spójnikami  (negacja 

, koniunkcja  , równowaŜność 

, implikacja 

W  stwierdzeniach  mogą  pojawiać  się  zmienne  związane  kwantyfikatorami 

, egzystencjalny  ).  

W Prologu nie uŜywa się kwantyfikatorów; w zamian za to stwierdzenia zapisuje się 

Ogólna postać klauzuli jest następująca:  

 

to poprzednik klauzuli, zaś 

KaŜde stwierdzenie moŜna zapisać w formie klauzuli. Istnieje algorytm, który to robi. 
W Prologu uŜywa się postaci jeszcze prostszej — klauzul Horna, czyli klauzul, które 
w następniku mają zero lub jeden term (  = 0 lub  = 1).  
Klauzula Horna to klauzula, w której co najwyŜej jeden element jest 
Przykładem  takiej  klauzuli  jest  {p,¬r,¬q}.  Klauzule  Horna  zapisuje  się  zwykle 
w postaci implikacyjnej:  p ← r 

∧ q, a w prolog wygląda to następująco

Klauzule z termem w następniku („z głową”) wyraŜają zaleŜności (dla 

Klauzule z pustym następnikiem („bez głowy”) są uŜywane do wyraŜenia celu 
czyli tego, co ma zostać udowodnione. 

Jest to metoda wnioskowania, którą moŜna stosować do opisanych tu stwierdzeń. 
Podstawowa reguła jest następująca: wiedząc Ŝe P 

Q oraz R 

S, wnioskujemy Ŝe P 

le tylko Q i R dają się zunifikować.  

Z  technicznego  punktu  widzenia,  osiąga  się  to  przez  wyliczenie  Q 
i usunięcie termów, które występują po obydwu stronach T.  

Programowanie  w  prologu  polega  na  zapisywaniu  stwierdzeń  uŜywając  predykatów 

pierwszego rzędu. Wyniki powstają jako rezultat (automatycznego wnioskowania).  

to funktor (symbol stanowiący nazwę relacji) z listą parametrów atomowych, np. 

ZauwaŜmy, Ŝe stała jest szczególnym przypadkiem termu (bezparametrowym).  

to  jeden  lub  więcej  termów  połączonych  spójnikami  (negacja 

).  

związane  kwantyfikatorami 

W Prologu nie uŜywa się kwantyfikatorów; w zamian za to stwierdzenia zapisuje się 

 — następnik.  

KaŜde stwierdzenie moŜna zapisać w formie klauzuli. Istnieje algorytm, który to robi.  

klauzul Horna, czyli klauzul, które 

Klauzula Horna to klauzula, w której co najwyŜej jeden element jest  niezanegowany. 

}.  Klauzule  Horna  zapisuje  się  zwykle  

q, a w prolog wygląda to następująco

:  p :- r, q.

 

Klauzule z termem w następniku („z głową”) wyraŜają zaleŜności (dla 

> 0) lub 

głowy”) są uŜywane do wyraŜenia celu — 

Jest to metoda wnioskowania, którą moŜna stosować do opisanych tu stwierdzeń.  

S, wnioskujemy Ŝe P 

Z  technicznego  punktu  widzenia,  osiąga  się  to  przez  wyliczenie  Q  S  oraz  P  R  

background image

• 

Występowanie  zmiennych  w  stwierdzeniach  powoduje,  Ŝe  w  trakcie  rezolucji  trzeba 
znaleźć  takie  wartości  dla  tych  zmiennych,  które  pozwolą  na  odpowiednie 
dopasowanie.  

• 

Unifikacja to proces znajdowania wartości dla zmiennych, dzięki którym uzyskamy Q 
= R.  

• 

Podstawianie  pod  zmienne  tymczasowych  wartości  pozwalających  na  unifikację 
zwane jest instancjonowaniem. Mówimy teŜ o utoŜsamieniu zmiennej z wartością.  

• 

Unifikacja  często  wymaga  nawrotów:  Zmienna  jest  instancjonowana,  lecz 
dopasowanie nie udaje się. Zmienną instancjonuje się wówczas inną wartością itd.  

 

 

 

Program w PROLOG-u jest zbiorem klauzul Horna: 

 
L

1

 ^ . . . ^ L

n−1

 → L

n

 
Gdzie  L

1

  do 

Ln−1

  to  poprzedniki  klauzuli  natomiast  L

n

  to  następnik  klauzuli,  klauzule  mogą  być  

z  poprzednikami  lub  bez.  Klauzule  z  poprzednikami  reprezentują  zaleŜności,  klauzule  bez 
poprzedników reprezentują fakty (prawda jest, ze L).  
 
Klauzule  bez  następnika  nie  występują  bezpośrednio  w  programie,  natomiast  moŜna  je 
wykorzystywać w konwersacji (po kompilacji programu) jako cel wnioskowania.  
 
Proces wnioskowania w języku Prolog opiera sie na specyficznym rodzaju rezolucji: 
 
Podstawowa reguła jest następująca:  
 
wiedząc ze z P wynika Q oraz z R wynika S, wnioskujemy ze z P wynika S, tylko wtedy, gdy Q i R 
dadzą się zunifikować. 
 
Jeśli  w  wyraŜeniach  występują  zmienne  to  w  trakcie  wnioskowania  znajduje  sie  takie  wartości 
zmiennych dla których Q=R. Taki proces nazywa sie właśnie unifikacja. Poszukiwanie takich wartości 
zmiennych, dla których Q=R polega na ukonkretnianiu wartości kolejnych zmiennych stałymi. Proces 
unifikacji  zazwyczaj  wiąŜe  sie  z  wieloma  próbami  podstawiania  (nawrotami),  gdy  ukonkretnianie 
zmiennych pewnymi stałymi nie udaje się. Wnioskowanie w języku PROLOG polega na dowodzeniu 
twierdzeń (hipotez). Hipotezy dowodzi sie korzystając z rezolucji, czyli przekształcania klauzul aŜ do 
doprowadzenia do powstania sprzeczności.  
 

Przykłady zadań wraz z rozwiązaniami 

 

Mamy zadanie logiczne o następującej treści: 

•  Przy ulicy stoją trzy domy:  
•  kaŜdy z nich ma inny kolor:  
•  jeden jest czerwony, drugi zielony a trzeci niebieski.  
•  W kaŜdym z tych domów mieszka osoba innej narodowości:  

w jednym Anglik, w drugim Hiszpan a w trzecim Japończyk.  

•  KaŜdy z nich hoduje jakieś zwierzę: jedna osoba ma ślimaka, druga jaguara a trzecia zebrę. 

background image

•  Wiadomo  jeszcze,  ze  Anglik  mieszka  w  czerwonym  domu,  Hiszpan  ma  jaguara,  Japończyk 

mieszka  na  prawo  od  właściciela  ślimaka,  a  właściciel  ślimaka  mieszka  na  lewo  od 
niebieskiego domu.  

 
Pytanie brzmi: Kto hoduje zebrę? 

 

Koncepcja rozwiązania zadania 

 
Rozwiązanie  tej  zagadki  w  duŜej  mierze  opiera  sie  na  znalezieniu  odpowiedniej  struktury 

danych,  która  pozwoliłaby  nie  tylko  wyrazić  to,  kto  w  którym  domu  mieszka  ale  takŜe  relacje  „na 
prawo  od”  i  „na  lewo  od”.  Implementacja  rozwiązania  w  klasycznym  proceduralnym  języku  
programowania  jest  dość  pracochłonna  i  wymaga  dodatkowo  implementacji  algorytmu,  który 
przeszukałby  przestrzeń  układów  mieszkańców  i  kolorów  tych  domów.  Najprostszy  jest  oczywiście  
w  takiej  sytuacji przegląd zupełny,  który  sprawdza  wszystkie  moŜliwe  stany.  Niestety jak to  zwykle 
bywa  algorytm  najprostszy  jest  najmniej  efektywny:  Dla  tak  prostego  zadania  jest  216  moŜliwych 
stanów do sprawdzenia, gdzie dla pełnej wersji tej zagadki (5 domów i pięć cech do spełniania) jest 
ich aŜ 24883200000. Zrozumiałe jest, Ŝe do takiego zadania lepiej jest wykorzystać algorytm bardziej 
skomplikowany ale pozwalający na znaczną redukcję stanów do sprawdzenia. Próbując rozwiązać to 
zadanie  wykorzystując  język  PROLOG  nie  ma  konieczności  implementacji  algorytmu,  bo  z  tym 
poradzi  sobie  kompilator  natomiast  waŜne  jest  by  w  sposób  właściwy  opisać  obiekty  i  zaleŜności  
w zagadce. 
Zadanie  to  moŜna  zrealizować  na  kilka  sposobów,  poniŜej  zostanie  zaprezentowany  jeden  z  nich. 
Pierwszym  krokiem  powinno  być  zdefiniowanie  sposobu  zakodowania  istniejących  zaleŜności.  Na 
pierwszy  rzut  oka  wydaje  sie,  ze  naleŜałoby  zdefiniować  predykaty  opisujące  relacje  miedzy 
obiektami  ale  moŜna  to  zadanie  rozwiązać  w  inny  sposób.  Opiera  sie  on  na  spostrzeŜeniu,  Ŝe  
w zagadce relacje moŜna uprościć do takiej postaci: „występują razem”, „na lewo”, „na prawo”, czyli 
jeśli  człowiek  mieszka  w  którymś  domu  to  człowiek  i  dom  występują  razem,  jeśli  człowiek  hoduje 
któreś zwierze, to teŜ występują razem, jeśli człowiek mieszka na prawo od hodowcy ślimaka tzn. Ŝe 
miedzy ślimakiem a tym człowiekiem jest relacja „na prawo”. Kolejne spostrzeŜenie wiąŜe się z tym, 
Ŝe  skoro  w  zagadce  są  relacje  na  lewo  i  na  prawo  to  znaczy,  ze  domy  są  ustawione  w  jednej  linii,  
w związku z czym moŜna je, na uŜytek rozwiązania, ponumerować i oznaczyć tak, ze dom na prawo 
ma  numer  większy  od  numeru  na  lewo.  Skoro  domy  zostały  ponumerowane,  to  numery  te  moŜna 
równieŜ podstawić pod ich mieszkańców, czyli jeśli dom o takim a nie innym kolorze ma numer jeden 
to mieszkańcom tego domu (człowiekowi i zwierzęciu) moŜna teŜ ten numer przypisać. Mając takie 
załoŜenia moŜna znów zredukować liczbę relacji i zauwaŜyć w takim razie, ze nie są konieczne relacje 
„występują  razem”,  „na  lewo”,  i  „na  prawo”  poniewaŜ  wystarczy  porównać  numery  domów 
przypisane  kaŜdemu  obiektowi.  W  związku  z  tym  zamiast  definiować  stałe  odpowiadające 
poszczególnym  obiektom  moŜna  załoŜyć,  Ŝe  kaŜdy  z  obiektów  będzie  opisany  przez  jedna  zmienną 
(Anglik,  Hiszpan,  Japonczyk,  Czerwony,  Zielony,  Niebieski,  Zebra,  Slimak,  Jaguar)  a  wartością 
kaŜdej  zmiennej  będzie  numer  domu.  Wystarczy,  wobec  tego,  zdefiniować  zakres  wartości  jaki  te 
zmienne mogą przyjmować (predykat „dom(numer domu)” ) i moŜna definiować zaleŜności: 
 

Przykładowy program rozwiązujący zadanie: 

dom(1). 

dom(2). 

dom(3). 

zebra(Zebra,Jaguar,Slimak,Czerwony,Niebieski,Zielony,Anglik,Japonczyk,Hiszpan):- 

dom(Zebra), 

dom(Jaguar), 

dom(Slimak), 

background image

dom(Czerwony), 

dom(Niebieski), 

dom(Zielony), 

dom(Anglik), 

dom(Japonczyk), 

dom(Hiszpan), 

Zebra =\= Jaguar, 

Zebra =\= Slimak, 

Jaguar =\= Slimak, 

Czerwony =\= Niebieski, 

Czerwony =\= Zielony, 

Zielony =\= Niebieski, 

Anglik =\= Japonczyk, 

Anglik =\= Hiszpan, 

Japonczyk =\= Hiszpan, 

Czerwony = Anglik, 

Jaguar = Hiszpan, 

Japonczyk > Slimak, 

Slimak < Niebieski . 

 

Jak działa powyŜszy program?  
Pierwsze trzy definicje deklarują, ze jednoargumentowy predykat „dom” spełniony będzie dla trzech 
wartości  argumentu  (1,2,3).  Określa  się  w  ten  sposób  zakres  wartości  jaki  moŜe  mieć  numer  domu. 
Dziewięcioargumentowy  predykat  „zebra”  jest  właściwym  opisem  problemu.  Dziewięć  zmiennych 
oznacza  tu  wszystkie  obiekty  jakie  występują  w  zagadce  tzn.:  Anglika,  Hiszpana,  Japończyka, 
czerwony  dom,  niebieski  dom,  zielony  dom  oraz  jaguara,  ślimaka  i  zebrę.  Wartość  z  jaką  ukonkret-
nione  będą  te  zmienne  będzie  oznaczało  numer  domu,  który  dany  obiekt  ma  lub  w  nim  mieszka. 
Pierwsze  dziewięć  warunków  określa  zakres  wartości  wszystkich  zmiennych.  Kolejne  trzy  warunki 
zastrzegają,  Ŝe  kaŜde  zwierzę  mieszka  w  innym  domu  (numery  domów  przypisane  pod  zmienne 
muszą być róŜne), kolejne trzy zastrzegają, ze dom o kaŜdym  kolorze ma inny numer a kolejne trzy 
warunki zastrzegają, Ŝe kaŜda osoba mieszka w innym domu. 
 
W warunku: 
Czerwony = Anglik, 
 
zastrzega  się,  ze  w  czerwonym  domu  mieszka  Anglik  (wartość  podstawiona  pod  kolor  domu,  czyli 
jego numer jest równy wartości podstawionej pod zmienną Anglik).  
 
W warunku: 
 
Jaguar = Hiszpan, 
 
zastrzega  sie,  ze  Hiszpan  ma  jaguara,  natomiast  ostatnie  dwa  warunki  informują,  ze  Japończyk 
mieszka  na  prawo  od  ślimaka  (numer  domu  Japończyka  jest  większy  od  numeru  domu,  w  którym 
mieszka ślimak) i ślimak mieszka na lewo od niebieskiego domu. 
Taka definicja zagadki jest wystarczająca i opisuje wszystkie zaleŜności jakie w zagadce występują.  
 
 
 
 

background image

Po zadaniu zapytania: 
 
?- zebra(Zebra, Jaguar, Slimak, Czerwony, Zielony, Niebieski, 

Anglik, Japonczyk, Hiszpan). 

Otrzymamy rozwiązanie zagadki. 

 

Problem n-hetmanów 

W  roku  1850  wybitny  matematyk  Carl  Friedrich  Gauss  sformułował  następujące 

zadanie:  Ustawić  na  szachownicy  osiem  hetmanów  tak  aby  Ŝaden  z  nich  nie  bił 
któregokolwiek  z  pozostałych.  Na  Rysunku  1  przedstawiono  jedno  z  jego  92  rozwiązań. 
Zadanie  o  hetmanach  moŜna  uogólnić  na  szachownice  dowolnych  rozmiarów.  Dalej 
będziemy  zajmować  się  takim  uogólnionym  zadaniem,  czyli  będziemy  szukać  ustawienia  
n hetmanów na szachownicy o n wierszach i n kolumnach. Uogólnione zadanie o hetmanach 
naleŜy  do  klasy  problemów  NP-trudnych.  Trudność  (dokładniej  wysoka  czasochłonność) 
znalezienia  rozwiązania  problemu  n-hetmanów,  przy  jednoczesnej  prostocie  jego 
sformułowania,  przyczyniła  się  do  tego,  Ŝe  stał  się  on  jednym  z  najczęściej  uŜywanych 
problemów  do  testowania  ogólnych  metod  poszukiwania  rozwiązania  trudnych  problemów. 
 

 

Rys.1 Przykładowe rozwiązanie – problem ośmiu hetmanów 

 

Przykładowy program rozwiązujący problem ośmiu hetmanów: 

queens([]).                                 % dodanie do pustej listy – jest rozwiązanie 

 

queens([ Row/Col | Rest]) :-                % w przeciwnym razie, dla każdego wiersza 

            queens(Rest),                   % połóż hetmana na najwyżej numerowanych wierszach 

            member(Col, [1,2,3,4,5,6,7,8]), % wybierz jedną możliwą kolumnę 

            safe( Row/Col, Rest).           % i sprawdź, czy jest bezpieczna, 

                                            % jeśli nie, powróć i sprawdź inną kolumnę, dopóki 

                                 % nie sprawdzisz wszystkich kolumn, jeśli błąd to powróć do poprzedniego wiersza 

                                             

background image

 

safe(Anything, []).                         % pusta szachownica jest zawsze bezpieczna 

 

safe(Row/Col, [Row1/Col1 | Rest]) :- % sprawdź, czy atakujesz hetmana w następnym wierszu poniżej 

            Col =\= Col1,                   % ta sama kolumna ? 

            Col1 - Col =\= Row1 - Row,      % sprawdź przekątną 

            Col1 - Col =\= Row - Row1, 

            safe(Row/Col, Rest).            % nie atakuje w następnym wierszu, sprawdź resztę szachownicy 

 

%member(X, [X | Tail]).                      % czytaj kolejne wartości kolumn 

 

%member(X, [Head | Tail]) :- 

 %           member(X, Tail). 

 

board([1/C1, 2/C2, 3/C3, 4/C4, 5/C5, 6/C6, 7/C7, 8/C8]). % prototyp szachownicy 

 

Różniczkowanie symboliczne 

 

W  matematyce  przez  różniczkowanie  symboliczne  rozumiemy  przekształcenie  jednego 

wyrażenia  algebraicznego  na  inne,  nazywane  pierwszą  pochodną  funkcji.  Załóżmy,  że  U  oznacza 

wyrażenie algebraiczne ze zmienną x. Pochodną U względem x zapisujemy jako: 

     





Pochodną wyznaczamy stosując rekurencyjne pewne zasady przekształcania wyrażeń do wyrażenia 

U. Warunki graniczne są określone następująco: 

    





 0, 

    




 1, 

c – stała. 

Wybrane pochodne funkcji (U,V – wyrażenia): 

• 

 



 



• 

  














• 

  












• 

 



  





, 

• 

 



  




   





background image

• 











 





• 

 





 

 







, 

• 

 







 









PowyŜsze  zestawy  reguł  łatwo  przekształcić  w  klauzule  Prologu,  gdyŜ  wyraŜenia  algebraiczne 
moŜemy  zapisać  jako  struktury,  a  operatorów  uŜyć  jako  funktorów.  MoŜemy  teŜ  korzystać  
z dopasowania wzorców, dopasowując cele do głów reguł. 

Cel d(E,X,F) nie zawiedzie, jeśli pochodną wyraŜenia E względem zmiennej X jest wyraŜenie F. 
Wprawdzie  operatory  +,-,*,/  są  juŜ  wbudowane,  ale  musimy  zadeklarować  operator  ^,  taki,  Ŝe 
X^Y oznaczać będzie x

y

. Deklaracje operatorów ułatwią na odczytywanie wyraŜeń.  

Przykładowy program obliczający pochodne: 

:-op(300,yfx,^). 

d(X,X,1):-!. 

d(C,_,0):-atomic(C). 

d(-U,X,-A):-d(U,X,A). 

d(U+V,X,A+B):-d(U,X,A),d(V,X,B). 

d(U-V,X,A-B):-d(U,X,A),d(V,X,B). 

d(C*U,X,C*A):-atomic(C),\+C=X,d(U,X,A),!. 

d(U*V,X,B*U+A*V):-d(U,X,A),d(V,X,B). 

d(U/V,X,A):-d((U*V)^(-1),X,A). 

d(U^C,X,C*U^(C-1)*W):-atomic(C),\+C=X,d(U,X,W). 

d(log(U),X,A*U^(-1)):-d(U,X,A). 

Jak obliczyć pochodną funkcji ?  

?- d(x+1,x,X). 

 

Zadania: 

1.  Oblicz  pochodne  kilku  wybranych  funkcji,  dokonaj  interpretacji  uzyskanych 

wyników, 

2.  Zastanów się, jak moŜna usprawnić sposób wypisywania wyników róŜniczkowania, 
3.   Utwórz  zestaw  predykatów,  który  pozwoli  na  wykonywania  operacji  +,-,/,*  na 

ułamkach  prostych,  zapisanych  w  postaci 

 !" #

$ %"&" #

.  Wynik  powinien  zostać 

przedstawiona  w  najprostszej  postaci,  naleŜy  równieŜ  uwzględnić  sytuacje 
wyjątkowe.  

4.  Utwórz  zestaw  predykatów,  który  pozwoli  na  wykonywania  operacji  +,-,/,*  na 

liczbach zespolonych.