Wprowadzenie do Sztucznej Inteligencji Laboratorium lista 0.2 Elementy języka Prolog: reguły i rekurencja. Przemysław Kobylański

Podobne dokumenty
Wprowadzenie do Sztucznej Inteligencji Laboratorium lista 0.1 Elementy języka Prolog: fakty i zapytania. Przemysław Kobylański

Programowanie dynamiczne

PROLOG. Prolog. Programowanie, W.F. Clocksin, C.S. Mellish, HELION Prolog, język sztucznej inteligencji, Eugeniusz Gatnar, Katarzyna Stąpor, Wyd.

PODSTAWY SZTUCZNEJ INTELIGENCJI

Programowanie w Logice Struktury danych (Lista 2)

Programowanie deklaratywne

Celem ćwiczenia jest zapoznanie się z podstawowymi możliwościami języka Prolog w zakresie definiowania faktów i reguł oraz wykonywania zapytań.

PROLOG INNE PRZYKŁADY MACIEJ KELM

Obliczenia na stosie. Wykład 9. Obliczenia na stosie. J. Cichoń, P. Kobylański Wstęp do Informatyki i Programowania 266 / 303

Obliczenia iteracyjne

Wstęp do programowania

Programowanie deklaratywne

Matematyka Dyskretna 2/2008 rozwiązania. x 2 = 5x 6 (1) s 1 = Aα 1 + Bβ 1. A + B = c 2 A + 3 B = d

Rekurencja. Przykład. Rozważmy ciąg

Programowanie w logice

ALGORYTMY I STRUKTURY DANYCH

Paradygmaty programowania

FUNKCJA LINIOWA - WYKRES

7. Pętle for. Przykłady

Zapisywanie w wybranej notacji algorytmów z warunkami i iteracyjnych

Laboratorium przedmiotu Paradygmaty Programowania

Przeszukiwanie z nawrotami. Wykład 8. Przeszukiwanie z nawrotami. J. Cichoń, P. Kobylański Wstęp do Informatyki i Programowania 238 / 279

Programowanie w Logice

Języki programowania deklaratywnego

Paradygmaty dowodzenia

Algorytmy i struktury danych

Programowanie i techniki algorytmiczne

Programowanie - wykład 4

Temat 5. Programowanie w języku Logo

Wykład 8. Rekurencja. Iterować jest rzeczą ludzką, wykonywać rekursywnie boską. L. Peter Deutsch

Podstawy programowania 2. Temat: Funkcje i procedury rekurencyjne. Przygotował: mgr inż. Tomasz Michno

Wykład 4. Określimy teraz pewną ważną klasę pierścieni.

Metoda tabel semantycznych. Dedukcja drogi Watsonie, dedukcja... Definicja logicznej konsekwencji. Logika obliczeniowa.

Wprowadzenie do Prologa

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Pochodna i różniczka funkcji oraz jej zastosowanie do obliczania niepewności pomiarowych

Języki programowania deklaratywnego

Programowanie w Logice Przykłady programów. Przemysław Kobylański

Programowanie w Logice

FUNKCJA LINIOWA - WYKRES. y = ax + b. a i b to współczynniki funkcji, które mają wartości liczbowe

Przykładami ciągów, które Czytelnik dobrze zna (a jeśli nie, to niniejszym poznaje), jest ciąg arytmetyczny:

Wykład 11a. Składnia języka Klasycznego Rachunku Predykatów. Języki pierwszego rzędu.

Zadanie 1. Suma silni (11 pkt)

Klasa 2 INFORMATYKA. dla szkół ponadgimnazjalnych zakres rozszerzony. Założone osiągnięcia ucznia wymagania edukacyjne na. poszczególne oceny

Logika Stosowana. Wykład 1 - Logika zdaniowa. Marcin Szczuka. Instytut Informatyki UW. Wykład monograficzny, semestr letni 2016/2017

Programowanie w Logice Środowisko programowania i dialog z systemem (Lista 0)

Języki programowania zasady ich tworzenia

Dedukcyjne bazy danych i rekursja

Rekurencja (rekursja)

Indukcja. Materiały pomocnicze do wykładu. wykładowca: dr Magdalena Kacprzak

Wyszukiwanie binarne

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Rekurencja. Rekurencja zwana także rekursją jest jedną z najważniejszych metod konstruowania rozwiązań i algorytmów.

Programowanie strukturalne i obiektowe. Funkcje

Poprawność semantyczna

Pochodna funkcji odwrotnej

//warunki początkowe m=500; T=30; c=0.4; t=linspace(0,t,m); y0=[-2.5;2.5];

Zapisywanie algorytmów w języku programowania

5. OKREŚLANIE WARTOŚCI LOGICZNEJ ZDAŃ ZŁOŻONYCH

Znaleźć wzór ogólny i zbadać istnienie granicy ciągu określonego rekurencyjnie:

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Temat 20. Techniki algorytmiczne

Podstawy programowania funkcjonalnego

Metoda Tablic Semantycznych

Wieczorowe Studia Licencjackie Wrocław, Wykład nr 6 (w oparciu o notatki K. Lorysia, z modyfikacjami) Sito Eratostenesa

Pochodna i różniczka funkcji oraz jej zastosowanie do rachunku błędów pomiarowych

I. Podstawy języka C powtórka

Programowanie w języku C++ Podstawowe paradygmaty programowania

Wykład z równań różnicowych

METODY DOWODZENIA TWIERDZEŃ I AUTOMATYZACJA ROZUMOWAŃ

Lab 10. Funkcje w argumentach funkcji metoda Newtona. Synonimy nazw typów danych. Struktury. Tablice struktur.

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

Adam Meissner STUCZNA INTELIGENCJA

Projekt 4: Programowanie w logice

Programowanie w środowisku Baltie

1 Funkcje uniwersalne

Zadanie 3 Oblicz jeżeli wiadomo, że liczby 8 2,, 1, , tworzą ciąg arytmetyczny. Wyznacz różnicę ciągu. Rozwiązanie:

Uwagi dotyczące notacji kodu! Moduły. Struktura modułu. Procedury. Opcje modułu (niektóre)

Lekcja : Tablice + pętle

REKURENCJA W JĘZYKU HASKELL. Autor: Walczak Michał

Prolog 2 (Filip Wroński, Łukasz Betkowski, Paweł Świerblewski, Konrad Kosmatka)

Algorytmy i struktury danych. Wykład 4

Programowanie deklaratywne

JAVAScript w dokumentach HTML (1) JavaScript jest to interpretowany, zorientowany obiektowo, skryptowy język programowania.

Przykładowe zadania z teorii liczb

Rozdział 4 KLASY, OBIEKTY, METODY

Klasy abstrakcyjne i interfejsy

Funkcje i instrukcje języka JavaScript

Klasyczny rachunek predykatów

[WYSYŁANIE MAILI Z PROGRAMU EXCEL]

Systemy ekspertowe i ich zastosowania. Katarzyna Karp Marek Grabowski

Programowanie w logice Prolog 2

WYRAŻENIA ALGEBRAICZNE

Prolog (Pro-Logic) Programowanie w Logice. Dr inż. Piotr Urbanek

EGZAMIN MATURALNY 2012 INFORMATYKA

6. Pętle while. Przykłady

Wyjątki. Streszczenie Celem wykładu jest omówienie tematyki wyjątków w Javie. Czas wykładu 45 minut.

Podstawy programowania. Wykład: 13. Rekurencja. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Algorytmika i pseudoprogramowanie

Luty 2001 Algorytmy (7) 2000/2001

Transkrypt:

Wprowadzenie do Sztucznej Inteligencji Laboratorium lista 0.2 Elementy języka Prolog: reguły i rekurencja Przemysław Kobylański Część I Wprowadzenie 1 Reguły Przypomnijmy z poprzedniej listy zadań fakty z pliku olimp.pl: rodzice(uranus, gaia, rhea). rodzice(uranus, gaia, cronus). rodzice(cronus, rhea, zeus). rodzice(cronus, rhea, hera). rodzice(cronus, rhea, demeter). rodzice(zeus, letom, artemis). rodzice(zeus, leto, apollo). rodzice(zeus, demeter, persephone). Aby zdefiniować predykaty ojciec/2 i matka/2 można użyć następujących dwóch reguł: ojciec(x, Y) :- rodzice(x, _, Y). matka(x, Y) :- rodzice(_, X, Y). Reguła ma postać Head :- Body. Gdzie Head jest formułą atomową wyrażającą warunek, który zachodzi jeśli spełniony jest warunek Body, przy czym warunek Body może być takiej postaci jak warunki w zapytaniach: może być formułą atomową; może być koniunkcją warunków oddzielonych przecinkami; może być alternatywą warunków oddzielonych średnikami; może być postaci W1 -> W2; W3 i należy go rozumieć następująco: Jeśli spełniony jest warunek W1, to sprawdzić czy spełniony jest warunek W2. W przeciwnym przypadku sprawdzić czy spełniony jest warunek W3. Poniżej zdefiniowano kilka predykatów wykorzystujących predykaty ojciec/2 i matka/2: 1

rodzic(x, Y) :- ojciec(x, Y). rodzic(x, Y) :- matka(x, Y). dziadek(x, Z) :- ojciec(x, Y), rodzic(y, Z). babcia(x, Z) :- matka(x, Y), rodzic(y, Z). 2 Poszukiwanie odpowiedzi Przedstawimy na przykładzie jak system Prolog poszukuje odpowiedzi na zadane pytanie. Należy zaznaczyć, że formalny opis działania programu w Prologu wymaga znajomości SLD-rezolucji będącej odmianą zasady rezolucji. Na potrzeby naszego kursu wystarczy jednak nieformalny opis zamieszczony poniżej. Załóżmy, że chcemy poznać która bogini X była babcią Zeusa:?- babcia(x, zeus). Warunek babcia(x, zeus) stanowi cel do udowodnienia. System poszukuje w programie czy zna definicję predykatu babcia/2 i znajduje regułę: babcia(x, Z) :- matka(x, Y), rodzic(y, Z). W Prologu zasięgiem zmiennej jest jeden fakt, jedna reguła lub jedno zapytanie. Z tego powodu zmienna X z zapytania i zmienna X z powyższej reguły są różnymi zmiennymi. System Prolog aby rozróżnić zmienne stosuje przemianowanie. My w tak krótki przykładzie nie będziemy przemianowywać zmiennych ale trzeba zdawać sobie sprawę, że jest to konieczne dla poprawnego działania programu. Warunek babcia(x, zeus) zostaje zastąpiony zgodnie z definicją i po podstawieniu zeus za zmienną Z. Uzyskujemy nowy cel: matka(x, Y), rodzic(y, zeus). Warunek matka/2 jest zdefiniowany jest regułą: matka(x, Y) :- rodzice(_, X, Y). Opierając się na powyższej regule możemy zastąpić cel matka(x, Y), rodzic(y, zeus) nowym celem: rodzice(_, X, Y), rodzic(y, zeus). W pierwszym fakcie definiującym warunek rodzice/3 zapisano, że rodzice(uranus, gaia, rhea), zatem po podstawieniu za X wartości gaia a za Y wartości rhea otrzymujemy nowy cel: rodzic(rhea, zeus). Warunek rodzic/2 zdefiniowany jest za pomocą dwóch reguł: rodzic(x, Y) :- ojciec(x, Y). rodzic(x, Y) :- matka(x, Y). i każda z nich musi być rozpatrzona osobno. System Prolog zabiera się w pierwszej kolejności za regułę zamieszczoną wcześniej w tekście programu (bliżej jego początku). Zatem aby sprawdzić czy rhea jest rodzicem zeusa zostanie sformułowany nowy cel do udowodnienia: ojciec(rhea, zeus). Reguła definiująca warunek ojciec/2: ojciec(x, Y) :- rodzice(x _, Y). wymaga odszukania wśród faktów definiujących warunek rodzice/3 takiego, w którym na pierwszej pozycji (pierwszym argumentem) jest stała rhea. Nie ma takiego faktu zatem skorzystanie z reguły rodzic(x, Y) :- ojciec(x, Y) 2

skończyło się niepowodzeniem i system Prolog przystępuje do próby skorzystania z drugiej reguły definiującej warunek rodzic/2: rodzic(x, Y) :- matka(x, Y). Tym razem nowym celem jest: matka(rhea, zeus). Zgodnie z regułą: matka(x, Y) :- rodzice(_, X, Y). konieczne jest odszukanie faktu definiującego warunek rodzice/3, który na drugiej pozycji miałby stałą rhea a na trzeciej stałą zeus. Jest taki fakt rodzice(cronus, rhea, zeus) zatem cel został udowodniony i może pojawić się odpowiedź: X = gaia Jako ciekawostkę można podać, że jeśli poprosimy system Prolog o znalezienie drugiej odpowiedzi, to po raz drugi otrzymamy tę samą odpowiedź:?- babcia(x, zeus). X = gaia ; X = gaia ; Nie jest to błędem, ponieważ system raz wywnioskował, że Gaia jest babcią jako matka matki Zeusa a drugi raz jako matka ojca Zeusa (rodzice Zeusa byli rodzeństwem). 3 Rekurencja W języku Prolog nie ma instrukcji pętli jaką znamy z języków imperatywnych (algorytmicznych). Naturalnym sposobem powtórzenia sprawdzania warunku jest jego wywołanie rekurencyjne. Rozpatrzmy warunek przodek(x, Y), który jest spełniony gdy X jest przodkiem dla Y. Pierwszą myślą jak wyrazić ten warunek w postaci programu jest napisanie następujących reguł: przodek(x, Y) :- rodzic(x, Y). przodek(x, Y) :- rodzic(x, Z1), rodzic(z1, Y). przodek(x, Y) :- rodzic(x, Z1), rodzic(z1, Z2), rodzic(z2, Y).... Wadą takiego podejścia jest to, że powyższa definicja wymaga nieskończenie wielu reguł. Możemy jednak zauważyć, że przodek to albo rodzic albo rodzic przodka. Zapisać możemy to w postaci następujących dwóch reguł: przodek(x, Y) :- rodzic(x, Y). przodek(x, Y) :- rodzic(x, Z), przodek(z, Y). Dla przykładu zapytajmy się czyim przodkiem był Cronus:?- przodek(cronus, X). X = zeus ; X = hera ; X = demeter ; 3

X = artemis ; X = apollo ; X = persephone ; X = persephone ; Tym razem dwukrotnie otrzymaliśmy odpowiedź, że był on przodkiem Persephone a to dlatego, że rodzice Persephone byli rodzeństwem. 4 Funkcje rekurencyjne Przedstawimy teraz kilka wybranych przykładów funkcji rekurencyjnych. 4.1 Silnia Zwróć uwagę w poniższej definicji predykatu fact/2 na obliczenie N-1 przed wywołaniem rekurencyjnym fact(n1, F1) i obliczenie N*F1 po powrocie z rekurencyjnego wywołania. fact(0, 1). fact(n, F) :- N > 0, N1 is N-1, fact(n1, F1), F is N*F1. Przykładowe obliczenia:?- fact(10, X). X = 3628800.?- fact(100, X). X = 9332621544394415268169923885626670049071596826438162146859296389521759999322 99156089414639761565182862536979208272237582511852109168640000000000000000000000 00. 4.2 Ciąg Fibonacciego Podobnie jak w przykładzie z obliczaniem silni musimy obliczyć nowe wartości parametrów przed wywołaniem rekurencyjnym, natomiast wynik obliczyć po powrocie z wywołania rekurencyjnego: fib(0, 0). fib(1, 1). fib(n, F) :- N > 1, N1 is N-1, N2 is N-2, fib(n1, F1), fib(n2, F2), F is F1+F2. Przykładowe obliczenia:?- fib(15, X). X = 610.?- fib(20, X). X = 6765.?- fib(25, X). X = 75025. Zwróć uwagę na długi czas poszukiwania odpowiedzi na trzecie pytanie. Czas poszukiwania odpowiedzi i liczbę kroków wnioskowania do tego potrzebnych można sprawdzić korzystając z predykatu time/1: 4

?- time(fib(15, X)). % 3,945 inferences, 0.001 CPU in 0.001 seconds (70% CPU, 6013720 Lips) X = 610.?- time(fib(20, X)). % 43,780 inferences, 0.144 CPU in 0.147 seconds (98% CPU, 305017 Lips) X = 6765.?- time(fib(25, X)). % 485,568 inferences, 23.148 CPU in 23.219 seconds (100% CPU, 20976 Lips) X = 75025. Jak widać obliczenie piętnastego wyrazu ciągu Fibonacciego trwało 0.001 sekundy, dwudziestego wyrazu 0.144 sekundy a dwudziestego piątego wyrazu aż 23.148 sekund gdyż potrzebnych było 485568 kroków wnioskowania. Czy pamiętasz z wcześniejszych kursów dlaczego tak długo trwa wyliczanie wartości wyrazów ciągu Fibonacciego ze wzoru rekurencyjnego? W punkcie o tablicowaniu wyników pokażemy jak w prosty sposób temu zaradzić. 5 Styl programowania Podane wcześniej przykłady predykatów nie były napisane zgodnie z zasadami dobrego stylu programowania. Otóż przyjęło się warunki z ciała reguły pisać w kolejnych wierszach stosując wcięcie. Dla przykładu predykat przodek/2 powinien zatem być zapisany następująco: przodek(x, Y) :- rodzic(x, Y). przodek(x, Y) :- rodzic(x, Z), przodek(z, Y). Natomiast predykat fib/2 następująco: fib(0, 0). fib(1, 1). fib(n, F) :- N > 1, N1 is N-1, N2 is N-2, fib(n1, F1), fib(n2, F2), F is F1+F2. 6 Tablicowanie wyników Powrócimy do przykładu z ciągiem Fibonacciego. Tym razem napiszemy predykat fib2/2, który będzie zachowywał się w sposób bardziej "inteligentny". Otóż jeśli ma on policzyć jakiś wyraz ciągu Fibonacciego, to najpierw będzie sprawdzał czy już liczył ten wyraz i jeśli tak, to odda wcześniej policzoną wartość a jeśli nie, to policzy wartość po raz pierwszy i zapamięta ją aby nie musieć liczyć po raz drugi. Już raz policzone wyniki będziemy zapamiętywać dodając nowe fakty, zatem predykat fib2/2 musi być zadeklarowany jako dynamiczny: 5

:- dynamic fib2/2. fib2(0, 0). fib2(1, 1). fib2(n, F) :- N > 1, N1 is N-1, N2 is N-2, fib2(n1, F1), fib2(n2, F2), F is F1+F2, asserta(fib2(n, F)). Jak widać jedyną różnicą w definicji powyższego predykatu jest dodanie zapamiętania faktu po wyliczeniu wyniku. Proszę zwrócić uwagę na konieczność użycia predykatu asserta/1 gdyż dodawanie na końcu definicji predykatem assertz/1 mijałoby się z celem (najpierw Prolog korzystałby z reguły zanim zauważyłby dodane na końcu fakty). O tym, że faktycznie obliczenia przyspieszyły można przekonać się analizując poniższy dialog:?- time(fib2(20, X)). % 96 inferences, 0.000 CPU in 0.000 seconds (75% CPU, 2000000 Lips) X = 6765.?- time(fib2(30, X)). % 50 inferences, 0.000 CPU in 0.000 seconds (71% CPU, 943396 Lips) X = 832040.?- time(fib2(50, X)). % 100 inferences, 0.000 CPU in 0.000 seconds (77% CPU, 1219512 Lips) X = 12586269025.?- time(fib2(100, X)). % 250 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 1111111 Lips) X = 354224848179261915075.?- time(fib2(1000, X)). % 4,500 inferences, 0.007 CPU in 0.008 seconds (91% CPU, 606714 Lips) X = 4346655768693745643568852767504062580256466051737178040248172908953655541794 90518904038798400792551692959225930803226347752096896232398733224711616429964409 06533187938298969649928516003704476137795166849228875.?- time(fib2(1000, X)). % 0 inferences, 0.000 CPU in 0.000 seconds (64% CPU, 0 Lips) X = 4346655768693745643568852767504062580256466051737178040248172908953655541794 90518904038798400792551692959225930803226347752096896232398733224711616429964409 06533187938298969649928516003704476137795166849228875. Zwróć uwagę, że kiedy po raz drugi zapytano się o o tysięczny wyraz ciągu Fibonacciego, to odpowiedź uzyskano natychmiast. 6

Część II Zadania i polecenia Zadanie 1 Załóżmy, że dany jest predykat przodek/2. Napisz predykat krewny/2, który wyraża relację między dwiema osobami, w której są tylko te pary dwóch różnych 1 osób, które mają wspólnego przodka. Zadanie 2 Załóżmy, że warunek is_a(object, Class) wyraża relację między obiektem a klasą do której ten obiekt należy, warunek a_kind_of(subclass, SuperClass) wyraża relację między podklasą a nadklasą, oraz warunek has(class, Property) wyraża relację między klasą a własnością jaką posiadają obiekty z tej klasy. Utwórz plik objects.pl zawierający następujące fakty: is_a(fig1, square). is_a(fig2, circle). a_kind_of(square, figure). a_kind_of(circle, figure). has(square, size). has(circle, radius). has(figure, color). Dopisz do pliku objects.pl definicję warunku subclass(subclass, Class), który zachodzi między podklasą a jej nadklasą (bezpośrednio lub pośrednio gdyż podklasa podklasy jest również podklasą). Dopisz do pliku objects.pl definicję warunku super_has/2, który zachodzi między obiektem a własnością jaką on posiada (uwzględnij dziedziczenie własności w podklasach). Na przykład w przypadku faktów z pliku objects.pl poprawne są następujące odpowiedzi:?- super_has(fig1, X). X = size ; X = color ;?- super_has(x, radius). X = fig2 ;?- super_has(x, color). X = fig1 ; X = fig2 ; 1 Warunek różne można sprawdzić predykatem \=/2. 7

Zadanie 3 Napisz w Prologu definicję funkcji 2 Ackermana: n + 1 gdy m = 0 i n 0, A(m, n) = A(m 1, 1) gdy m > 0 i n = 0, A(m 1, A(m, n 1)) gdy m > 0 i n > 0 Oblicz wartość powyższej funkcji dla m = 4 i n = 1. Czy udało się policzyć wynik? Zastosuj tablicowanie wyników i powtórz powyższe obliczenia. 2 Pamiętaj, że w Prologu funkcja to relacja między argumentami a wartością. 8