← ST-Computer 06 / 1987

EinfĂŒhrung in die Programmiermethoden und Sprachen der KI (4)

Kurs

Elemente der kĂŒnstlichen Intelligenz 4. Teil: Automatisches Beweisen

EinfĂŒhrung in die Problematik

In der letzten Folge haben wir zusammen die ersten logischen Terms umgeformt und dabei gesehen, daß Sprachen wie PROLOG und LISP die logischen Termumformungen weitgehend ĂŒbernehmen können. Viel interessanter ist natĂŒrlich das Problem, dem Computer die selbstĂ€ndige FĂŒhrung eines mathematischen Beweises unter Benutzung logischer Termumformungen zu ĂŒbertragen. Einem mathematisch wenig gebildeten Menschen bereitet es meist erhebliche Schwierigkeiten, einem mathematischen Beweis auch nur zu folgen. Einen mathematisch korrekten Beweisgang werden sich deshalb wohl auch die wenigsten Leser Zutrauen. Sie werden sicher zustimmen, wenn man ein GerĂ€t, das diese Beweise selbstĂ€ndig durchfĂŒhrt, als intelligent bezeichnet. Und da die Logik die „Haus- und Hofwissenschaft“ der Informatiker ist, ist es nicht weiter verwunderlich, wenn auf dem Gebiet der automatischen BeweisfĂŒhrung SpektakulĂ€res geleistet wurde.

Grundlagen der Aussagenlogik

ZunĂ€chst brauchen wir natĂŒrlich wieder einige Begriffe aus der Aussagenlogik. Ich darf bei dieser Gelegenheit wieder einmal auf das wirklich ausgezeichnete Buch von D. R. Hofstadter (siehe Literaturhinweise) hinweisen, in dem Sie eine sehr eigenwillige, gut lesbare EinfĂŒhrung in die Aussagenlogik finden. Generell ist es die Aufgabe eines Beweises, zu zeigen, daß eine wohlgeformte Aussage (d. h. eine Aussage, deren syntaktischer Aufbau den Regeln der Aussagenlogik genĂŒgt) immer den Wahrheitswert wahr erhĂ€lt, egal welchen Wahrheitswert die einzelnen in der Aussage vorhandenen Variablen annehmen. Z. B. ist die Aussage a v ~ a immer wahr, egal welchen Wert a annimmt. Um zu beweisen, daß diese Aussage immer wahr ist, wĂŒrde es genĂŒgen, wenn man die Wahrheitstafel der Aussage niederschreibt. Bei komplizierten Aussagen fĂŒhrt dieses Verfahren schnell an die Grenzen der Anschaulichkeit. Dann ist es einfacher, durch erlaubte Umformungen zum gewĂŒnschten Ergebnis zu kommen. Nehmen wir an, daß die AusdrĂŒcke a und b wohlgeformt sind. Dann sind es auch alle mit Hilfe der Junktoren ~ ,&,v gebildeten AusdrĂŒcke: ~ a, a v b, a & b. Außerdem ist natĂŒrlich ein atomarer Ausdruck wohlgeformt. Weiterhin gelten die folgenden Äquivalenzen, d. h., der Ausdruck rechts von = kann ersetzt werden durch den Ausdruck links von = und umgekehrt:

  1. ~ ~a = a
    Gesetz der doppelten Verneinung

  2. ~(a v b) = ~a & ~b
    De Morgan’sches Gesetz

  3. ~ (a & b) = ~ a v ~ b
    De Morgan’sches Gesetz

  4. a v (b & c) = (a v b) & (a v c )
    Distributivgesetz

  5. a -> b = ~avb
    Implikation

Die resultierenden AusdrĂŒcke sind dann wieder wohlgeformt. Weiterhin benötigt man zur Herleitung neuer Aussagen noch sogenannte Schlußregeln:

  1. Modus Ponens (bejahende Abtrennungsregel)

A —> b a b

Beispiel: Wenn es regnet, dann ist der Rasen feucht.
Es regnet.
Der Rasen ist feucht.

  1. Modus Tollens (verneinende Abtrennungsregel)
    a —> b ~b ~a

Beispiel: Wenn es regnet, dann ist der Rasen feucht.
Der Rasen ist nicht feucht.
Es regnet nicht.

  1. Kettenschluß

a —> b b —> c a —> c

Beispiel: Wenn Peter wĂŒtend ist, dann hat er Hunger.
Wenn Peter Hunger hat, dann ißt er Schokolade.
Wenn Peter wĂŒtend ist, dann ißt er Schokolade.

Die fĂŒr uns interessante Form eines Beweises ist der sogenannte indirekte Beweis. Hier untersucht man die Negation des zu beweisenden Ausdrucks. Gelangt man durch Umformung der Negation zu einem Widerspruch, gilt also die negierte Aussage nicht, dann gilt nach dem Prinzip der doppelten Verneinung die originale Aussage. Schauen wir uns als Beispiel den Beweis des Kettenschlusses an:

ZunÀchst negieren wir den Term:

~((a -> b) & (b -> c) -> (a -> c)) =
~(~ ((~ a v b) & (~ b v c)) v (~ a v c))

Diesen Term bringen wir in die konjunktive Normalform, d. h. wir formen ihn so um, daß er nur noch einzelne, durch & verbundene Terme enthĂ€lt:

~((~ (~ a v b) v ~ (~ b v c)) v (~ a v c)) =
(~ a v b)&(~ b v c) & ~(~a v c) =
(~ a v b)&(~ bvc)& a & ~c
(Term in konkjunktiver Normalform.

Diese konjunktive Normalform eines logischen Termes heißt auch Klauselform, die einzelnen durch & verbundenen Terme Klauseln (also z. B. (~ a v b)). Das Prinzip der Resolution besteht nun darin, zu zeigen, daß sich zwei Klauseln widersprechen, d. h. daß in einem logischen Term ein Unterterm der Form ...& x & ~x & ... vorhanden ist. Das im folgenden Kapitel erlĂ€uterte Resolutionsprinzip geht von diesem leicht zu beweisenden Theorem aus:

(a v x) & (~ a v y) —> xvy
(Resolutionsschritt)

Damit wĂŒrde sich der Term in kon-unktiver Normalform darstellen lassen als:

(~a v b)&(~b v c)&a & ~c = (a v 0) & (~ a v b) & (~ b v c) & ~ c —>
(0 v b) & (~ b v c) & ~c = b & (~b v c) & ~c —> c & ~c (Widerspruch!)

%--------------------------------------------------------------------- % Interpreter fĂŒr musterorientierte Programmierung % nach I. Bratko, PROLOG Programming for artificial Intelligence. :-op(800,xfx,--->). % Definiert den "AusfĂŒhrungsoperator" start :- % Dieses PrĂ€dikat sucht in der Bedingung ---> Aktion, % Datenbank nach einem Muster test(Bedingung), % [] ----> []. Alle Elemente der fĂŒhre_aus(Aktion). % ersten Liste werden als Bedingung % getestet. Wenn alle Bedingungen test([]). % erfĂŒllt sind, werden alle Aktionen test([Kopf|Rest]) :- % der zweiten Liste ausgefĂŒhrt, call(Kopf), test(Rest). fĂŒhre_aus([stop]):- told,abolish(klausel,1). fĂŒhre_aus([]):- start. fĂŒhre_aus([Kopf|Rest]) call(Kopf), fĂŒhre_aus(Rest). ersetze(A.B) :- retract(A), assert(B). %--------------------------------------------------------------------- % Übersetzung einer Aussage in Klauseln der Datenbasis :- op(100,fy,~). % Diese Direktiven definieren die logischen Operatoren :- op(110,xfy,&). % nicht C), und (&), oder (v) und die Implikation (=>) :- op(120,xfy,v). % Das erste Argument ist die PrioritĂ€t des Operators, :- op(130,xfy,=>). % Je kleiner die Zahl, desto grĂ¶ĂŸer die PrioritĂ€t. ĂŒbersetze(F & G) % Dieses PrĂ€dikat ĂŒbersetzt logische Terme in der ĂŒbersetze(F), % konjunktiven Normalform mit Hilfe der weiter unten ĂŒbersetze(G). % definierten erlaubten Umformungen. Ist keine weitere % Umformung mehr möglich, wird der Term als Klausel % abgespeichert. ĂŒbersetze(Aussage) forme_um(Aussage,NeueAussage),write('('),write(Aussage),write(') => ('), write(NeueAussage),write(')'),nl, ĂŒbersetze(NeueAussage). ĂŒbersetze(Aussage):- assert(klausel(Aussage)),write(klausel(Aussage)),nl. forme_um(~(~X),X) . % Prinzip der doppelten Negation forme_um(X => Y,~X v Y). % Die Implikation forme_um(~(X & Y),~X v ~Y). % 1. De Morgansches Gesetz forme_um(~(X v Y),~X & ~Y). % 2. De Morgansches Gesetz forme_um(X & Y v Z,(X v Z) & (Y v Z)). % 1. Distributivgesetz forme_um(X v Y & Z,(X v Y) & (X v Z)). % 2. Distributivgesetz forme_um(X v Y,X1 v Y) :- forme_um(X,X1). % Umformung von TeilausdrĂŒcken forme_um(X v Y,X v Y1) :- forme_um(Y,Y1). % " " " forme_um(~X,~Xl) forme_um(X,X1) . % " %--------------------------------------------------------------------- % Musterorientiertes Programm zur DurchfĂŒhrung der Resolution % Widerspruch gefunden: [klausel(X),klausel(~X)] ---> [write('Widerspruch in der Negation gefunden!'),nl, write('Aussage damit wahr.') ,nl,stop]. % (a v ~a v b) ist immer wahr. [klausel(C),in(P,C),in(~P,C)]---> [write (klausel(C)) , write(' ist immer wahr, wird also aus der Datenbank entfernt.'),nl, retract(klausel(C))]. % (a v a v b) = (a v b) Idempotenzgesetz vereinfacht Terme. [klausel(C),entferne(P,C,C1),in(P,C1)] ---> [write(klausel(C)), ersetze(klausel(C),klausel(C1)), write(' wird ersetzt durch ') .write(klausel(C1)),nl]. % Resolution Spezialfall: a & (~a v b) => b [klausel(P), klausel(C), entferne(~P,C,C1), not fertig (P,C,P) ]---> [assert(klausel(C1)),assert(fertig(P,C,P)),write('klausel('),write(P) write(’) & klausel(’),write(C),write(') => '),write(klausel(C1)),nl]. % Resolution Spezialfall: ~a & (a v b) => b [klausel(~P),klausel(C),entferne(P,C,C1),not fertig(~P,C,P)] ---> [assert(klausel(C1)), assert(fertig(~P,C,P)) ,write('klausel('),write ~P), write(') & klausel('),write(C),write('j => '),write(klausel(CI)),nl]. % Resolution allgemeiner Fall: (a v b) & (~a v c) => b v c [klausel(C1),entferne(P,C1,CA), klausel(C2),entferne(~P,C2,CB),not fertig(C1,C2,P)] ---> [assert(klausel(CA v CB)),assert(fertig(C1,C2,P)), write('klausel('),write(C1), write(') & klausel('),write(C2),write(') => '),write(klausel(CA v CB)),nl]. []----> [write('Kein Widerspruch in der Negation entdeckt!'),nl,stop]. entferne(X,X v Y,Y). % Entfernt X aus X v Y. Übrig bleibt Y. entferne(X,Y v X,Y). % entferne(X,Y v Z,Y v Z1) :- entferne(X,Z,Z1). % Entfernt Teilausdruck entferne(X,Y v Z,Y1 v Z) :- entferne(X,Y,Y1). % " ” in(X,X). % X ist in sich selbst enthalten. in(X,Y) :- entferne(X,Y,_). % X ist in Y enthalten, wenn es entfernt werden % könnte. %----------------------------------------------------- % automatisches Beweisen beweise(Aussage) :- ausgabe(GerĂ€t),teil(GerĂ€t),write(’Zu beweisen: '), write(Aussage),nl,nl,nl,write('Negation: '), write(~Aussage),nl,nl, ĂŒbersetze(~Aussage),start. ausgabe(printer). % Auf diesem GerĂ€t soll die Ausgabe des Beweises % erfolgen. Es sind alle legalen Datenströme (also % auch Dateien) möglich. end.

Listing 1: Definition einiger Operatoren

Das Resolutionsprinzip

Das Resolutionsprinzip stellt sich also folgendermaßen dar.

  1. Negiere den zu beweisenden Term.
  2. ÜberfĂŒhre den negierten Term in die Klauselform (konjunktive Normalform)
  3. FĂŒhre wenn möglich den Resolutionsschritt durch.
  4. Durchforste die Menge der Klauseln auf Widerspruch.
  5. Wenn ein Widerspruch entdeckt wurde, dann ist die nicht negierte Aussage bewiesen.

Das in Listing 1 dargestellte PROLOG-Programm fĂŒhrt die hier beschriebene Resolution durch. Es benutzt einen in PROLOG geschriebenen Musterinterpreter aus dem sehr interessanten Buch von I. Bratko. Das Programm ist von mir so verĂ€ndert worden, daß man jederzeit die Schlußweise der Maschine verfolgen kann. Gleichzeitig möchte ich an Hand dieses Programms die hervorragenden Eigenschaften von PROLOG bei der Definition eigener Operatoren erlĂ€utern.

Musterorientierte Programmierung

Unter musterorientierter Programmierung versteht man ein Programmschema, das nicht so starr reagiert wie die konventionellen Programmiermethoden. Normalerweise besteht ein Programm aus festen Modulen, deren Ablauf explizit vorgegeben ist. Ein musterorientiertes Programm besteht dagegen aus einer Sammlung von musterorientierten Modulen. Jedes Modul besteht aus zwei Teilen:

  1. einem Bedienungsmuster,
  2. Aktionen, die ausgefĂŒhrt werden sollen, wenn das Bedingungsmuster erfĂŒllt ist.

Die AusfĂŒhrung eines Moduls wird in Gang gesetzt, wenn ein passendes Muster in der Datenbasis entdeckt wird. Musterorientierte Programmierung kann damit also so etwas wie ein natĂŒrliches Modell fĂŒr parallele Datenverarbeitung angesehen werden, da jedes Modul auf einem separaten Prozessor implementiert werden kann, der auf die gemeinsame Datenbasis wirkt. Die ErfĂŒllung von Bedingungsmustern erinnert natĂŒrlich sofort an den PROLOG-Unifizierungsprozeß. In der Tat ist der PROLOG-Interpreter nichts anderes als ein musterorientiertes Programmsystem. Im Beispielprogramm Listing 1 werden die Module in der Form

[Bedingungen] — [aktionen]

dargestellt. Hierin bedeutet [Bedingungen] eine Liste von Bedingungen in klausaler Form und [aktionen] eine Liste mit PrĂ€dikaten, die im Falle eines erfolgreichen Tests ausgefĂŒhrt werden sollen. Das PrĂ€dikat Start setzt dann das musterorientierte Programm in Aktion, indem es das goal Bedingungen — Aktionen im PrĂ€dikat Start zu unifizieren versucht. Hat der PROLOG-Interpreter eine Instanz gefunden, dann wird ein Test ausgefĂŒhrt, der alle Klauseln in der Bedingungsliste zu unifizieren versucht. Ist das gelungen, dann werden die in der Liste Aktionen aufgefĂŒhrten PrĂ€dikate zur Unifizierung gebracht. Wenn alle PrĂ€dikate unifiziert worden sind, bleibt nur noch die leere Liste als Aktionsliste zurĂŒck. Dann wird durch erneuten Aufruf von Start nach dem nĂ€chsten Muster in der Datenbasis gesucht. Ist kein weiteres Muster vorhanden, hat der PROLOG-Interpreter einen Mißerfolg und beendet das PrĂ€dikat Start.

Das PrÀdikat op/3

Zur Funktionsweise des Programms in Listing 1 trĂ€gt in ganz besonderer Weise die Möglichkeit bei, in PROLOG eigene Operatoren zu vereinbaren. Dies geschieht mit Hilfe des PrĂ€dikates op/3. Die Syntax des Aufrufs ist op(PrioritĂ€t,AssoziativitĂ€t,Name). Hierin bedeutet PrioritĂ€t eine Zahl zwischen 1 und 1200. Die Bedeutung dieser Zahl wird klar, wenn man sich an die EselsbrĂŒcke Punktrechnung geht vor Strichrechnung erinnert. In PROLOG erkennt man diese EselsbrĂŒcke in Form der Vereinbarung op(500,yfx,+) bzw. op(400,yfx,A) wieder. Man sieht: Je Kleiner die PrioritĂ€t ist, desto eher wird der entsprechende Operator ausgewertet.

Die AssoziativitĂ€t beschreibt gleichzeitig die Stelligkeit und Position des neu definierten Öperators. Die AssoziativitĂ€t darf nur einen der folgenden Werte haben:

fx, fy, xfx, xfy, yfx, yfy, xf, yf

f steht fĂŒr Funktor und kennzeichnet seine Stellung innerhalb eines Terms: fx und fy geben an, daß der neue Operator in PrĂ€fix-Notation zu gebrauchen ist. xfx, xfy, yfy und yfx kennzeichnen einen in Infix-Notation zu verwendenden Operator. xf und yf schließlich sind die AssoziativitĂ€t eines Postfix-Operators. Wie man sieht, sind + und * also Infix-Operatoren, d. h. PROLOG erwartet das Additionszeichen bzw. Multiplikationszeichen zwischen zwei Zahlen oder Termen. Und was bedeutet x und y? Ein x gibt an, daß auf der betreffenden Seite alle Terme Operatoren mit niedrigerer PrioritĂ€t haben mĂŒssen. Ein y lĂ€ĂŸt dagegen auch Operatoren mit niedriger oder gleicher PrioritĂ€t zu. Um das zu verdeutlichen, schauen wir uns einen beliebigen arithmetischen Term an:

3*X + 7-Y/4

Dieser arithmetische Term ist Ă€quivalent zu ((3AX) + 7)-(Y/4). Ein Blick in TAb. 1 zeigt uns, warum auch PROLOG diese Interpretation mit uns teilt. Der erste Operator, auf den der Interpreter bei der Bearbeitung des Termes stĂ¶ĂŸt, ist ★. Auf der linken Seite ist alles klar, der einzige Operand ist das numerische Atom 3. Mehrdeutig könnte es auf der rechten Seite werden, wenn nicht die AssoziativitĂ€t des ★-Operators den Wert yfx hĂ€tte. Damit besteht die Forderung, daß rechts vom ★-Zeichen nur Operatoren kleinerer PrioritĂ€t stehen dĂŒrfen. + hat aber die PrioritĂ€t 500 - und die ist um 100 grĂ¶ĂŸer als die PrioritĂ€t 400, welche ★ besitzt. Folglich wird nur der Term 3^X bewertet und berechnet. Das Ergebnis steht nun zur VerfĂŒgung und

op(+,yfx,500). op(-,yfx,500). op(*,yfx,400). op(/,yfx,400).

Tabelle 1

Abb.1: Argumentbindung durch unterschiedliche PrioritÀten

der Interpreter nimmt diesen Wert als linke Seite des folgenden +-Operators. Da die rechte Seite nur kleinere und nicht gleiche PrioritĂ€t haben darf, gehört der auf - folgende Term nicht zur rechten Seite. Das Ergebnis ist als (3 ★ X) + 7. Dieses Ergebnis wiederum stellt nun die linke Seite des folgenden Subtraktionsoperators dar. Und da / eine niedrigere PrioritĂ€t besitzt als -, gehört der Term Y/4 in die rechte Seite des Subtraktionsoperators. Also wird jetzt erst der Term Y/4 bewertet. Die linke Seite des Divisionsoperators endet bei dem -Zeichen, da dieses eine höhere PrioritĂ€t besitzt. Also wird jetzt nur der Wert Y/4 berechnet. Das Ergebnis ist jetzt der rechte Teil des Subtraktionsoperators, der jetzt folgende Operation durchzufĂŒhren hat: ((3AX) + 7) - (Y/4). Abb. 1 zeigt diese Situation nochmals graphisch.

Im Programm Listing 1 werden nun die folgenden Operatoren definiert:

—> Der Aktionsoperator fĂŒr musterorientierte Programmierung. Er trennt die Bedingungsliste von der Aktionsliste.

~ Der Negationsoperator. Negiert die logischen Terme.

& Der Konjunktionsoperator. VerknĂŒpfung zweier Terme durch logisches und.

v Der Disjunktionsoperator. VerknĂŒpft zwei logische Terme durch oder.

—> Der Implikationsoperator a —> b heißt: Wenn a gilt, dann gilt auch b.

Die PrioritĂ€ten der entsprechenden Operatoren ergeben sich entsprechend den Regeln der Logik, wonach die Negation vor der Konjunktion vor der Disjunktion vor der Implikation zu berĂŒcksichtigen ist. Die Negation wird als PrĂ€fixoperator definiert (~ steht immer vor dem logischen Term, der negiert wird), die ĂŒbrigen Operatoren werden als Infixoperatoren definiert (d. h. sie stehen zwischen zwei Termen). Nach der Definiton dieser Operatoren ist PROLOG in der Lage, logische Terme korrekt zu bearbeiten.

Das PrÀdikat forma um/2

Diese PrÀdikate enthalten das Wissen um die Umformung logischer Terme.

So bedeutet z. B. forme_um(~(~X), X)., daß der Term ~(~X) durch den vereinfachten Term X ersetzt werden kann (Prinzip der doppelten Negation). Ähnliche Umformungen finden sich fĂŒr die Eliminierung der Implikation, die De Morgan’schen Gesetze und die Distributivgesetze. Falls ein Term noch Unterterme enthĂ€lt, die umgeformt werden können, sorgen die drei letzten Instanzen dieses PrĂ€dikates fĂŒr die entsprechende Umformung. Die Übersetzung eines Termes in Klauselform wird durch das PrĂ€dikat ĂŒbersetze/1 vorgenommen. Dieses ĂŒbersetzt einen Term in der Klauselform, indem es jeden einzelnen Term umformt. Ist keine weitere Umformung nach den Regeln forme_um mehr möglich, wird die so gefundene Formel in die Datenbasis aufgenommen.

:-beweise((mann(marcus) & pompejaner(marcus) & (pompejaner(X) => römer(X)) & herrscher(cĂ€sar) & (römer(X) => (loyal(X,cĂ€sar) v haßt(X,cĂ€sar))) & (mann(X) v herrscher(Y) v versucht_mord(X,Y) => ~ loyal(X,Y)) & versucht_mord(marcus,cĂ€sar)) => haßt(marcus,cĂ€sar)).

Listing 2: Die geschichtliche Frage an den Computer

Die Produktionsregeln fĂŒr das Resolutionsverfahren

Die Produktionsregeln, nach denen die Terme in konjunktiver Normalform abgearbeitet werden, sind in Form der oben beschriebenen Muster in der Datenbank enthalten. So lautet die Regel, die den Widerspruch in der Datenbank entdeckt:

[klausel(X),klausel(~X)] —> [write(’Widerspruch gefunden’),...]

Wenn also in der Datenbank sowohl die Klausel X als auch ihre Negation enthalten ist, dann liegt ein Widerspruch vor und die Aktionsliste druckt die entsprechende Mitteilung aus und stoppt die weitere AusfĂŒhrung des Programms. In den Produktionsregeln taucht u. a. das PrĂ€dikat fertig/3 auf. Es dient dazu, einen Überlauf in der Datenbank zu verhindern. Die Resolution produziert ja neue Klauseln: (a v b) & (~ a v c) —> (b v c). Bei Existenz zweiter Klauseln der Form (a v b) & (~ a v c) wird folglich eine neue Klausel (b v c) produziert und in die Datenbank aufgenommen. Da aber die alten Klauseln (a v b) und (~ a v c) nicht aus der Datenbank entfernt werden, wĂŒrde beim nĂ€chsten Aufruf der Resolution wieder die gleiche Klausel produziert werden. Um das zu verhindern, wird bei der ersten Produktion einer Klausel das PrĂ€dikat fertig((a v b),( ~ a v c),a). in die Datenbank aufgenommen, was eine nochmalige Resolution obiger Klauseln verhindert, die Resolution von Untertermen aber noch zulĂ€ĂŸt. Falls schließlich alle logischen Terme in der Datenbasis umgeformt sind und keine weitere Resolution mehr möglich ist, dann trifft PROLOG schließlich auf die leere Bedingungsliste und weiß, daß kein Widerspruch in der Datenbasis gefunden wurde. In diesem Fall ist der Beweis des zu beweisenden Satzes gescheitert! Zur Übung sollten Sie sich den Beweis (a — b)&(b — c) — (a—c) einmal vom Programm vorfĂŒhren lassen. Anschließend schauen wir uns die Arbeitsweise des Programms einmal an einem berĂŒhmten Beispiel an: Der Frage nĂ€mlich, ob Marcus CĂ€sar haßte.

Haßte Marcus CĂ€sar?

Dieses Beispiel stammt von Elaine Rieh und ist eine gute Demonstration fĂŒr die Instanzierung von Variablen in logischen Termen wĂ€hrend des Beweises. Hier die Postulate, von denen im Beweis ausgegangen wird:

mann(marcus). Marcus war ein Mann.

pompejaner(marcus). Marcus war ein Pompejaner.
pompejaner(X) —> römer(X). Alle Pompejaner waren Römer.
herrscher(cÀsar). CÀsar war ein Herrscher.
römer(X) —> loyal(X,cĂ€sar) v haßt (X,cĂ€sar). Alle Römer waren entweder CĂ€sar gegenĂŒber loyal oder sie haßten ihn.
mann(X) & herrscher(Y) & versucht_mord(X,Y)-> ~loyal(X,Y).
Wenn ein Mensch einen Herrscher zu ermorden versucht, dann ist er ihm nicht loyal gegenĂŒber, versucht_mord(marcus, cĂ€sar). Markus versuchte CĂ€sar zu ermorden.

Listing 2 zeigt nun die Eingabe, die fĂŒr den Beweis nötig ist, daß Marcus CĂ€sar haßte, und Abb. 2 zeigt die Arbeit des Programmes.

(Dr. Sarnow)

Literaturhinweise:

1.) Bratko, Ivan. PROLOG Programming for artificial Intelligence.
Addison Wesley Publishing. 1986.

2.) Hofstadter, Douglas R. Gödel, Escher, Bach: ein endlos geflochtenes Band.
Ernst Klett Verlage. 1985.

3.) Rieh, Elaine. Artificial Intelligence.
McGraw-Hill 1983.

Abbildung 2: Haßte Markus CĂ€sar? Ein neuzeitlicher Lösungsweg!