Projekt CAS
Aus ProgrammingWiki
Inhaltsverzeichnis |
Einführung
Die Programmiersprache Lisp ist bereits zum Ende der 1950er Jahre mit den Ziel entwickelt worden, symbolische Ausdrücke verarbeiten zu können. Damit wurde die Grundidee der heutigen Computeralgebrasysteme (CAS) vorweggenommen.
Wenn wir Gleichungen äquivalent umformen, die erste Ableitung einer Funktion "berechnen" oder ihr unbestimmtes Integral angeben, so führen wir bei diesen Standardaufgaben des Mathematikunterrichtes keine numerischen Berechnung aus. Vielmehr suchen wir ein Ergebnis, das "lediglich" der Manipulation von Symbolen entspricht. Diese unterliegt allerdings strengen Regeln.
Mit Scheme, das wir der Familie der Lisp-Sprachen zuordnen können, wollen wir zwei grundlegende Aufgaben moderner CAS realisieren:
- das symbolische Differenzieren sowie
- das Vereinfachen von Termen.
Natürlich soll anschließend mit diesen Termen auch (numerisch) gerechnet werden.
Beispiel:
(vereinfache-vollstaendig (ableitung '(+ (^ x 2) (/ 1 x)) 'x)) --> (+ (* 2 x) (/ -1 (^ x 2))) (berechne (ableitung '(+ (^ x 2) (/ 1 x)) 'x) 'x 2) --> 3 3/4
Ohne Einschränkungen der Leistungsfähigkeit wollen wir fordern, dass die mathematischen Ausdrücke aus
- atomaren Elementen (Symbole / Zahlen),
- einstelligen (unären) Operationen oder
- zweistelligen (binären) Operationen
bei beliebigen Termstrukturen bestehen.
Symbolisches Differenzieren
Zunächst sollten wir Bezeichner festlegen, die sich stärker am Mathematikunterricht orientieren:
Nun testen wir das vorliegende Programmfragment:
Exemplarisch untersuchen wir in diesem Fragment die Implementation der Produktregel
$y = u \cdot v \longrightarrow y' = u' \cdot v + u \cdot v'$
(list '+ (list '* (ableitung term1 var) term2) (list '* term1 (ableitung term2 var)))
Wir stellen fest: Die Produktregel wird unter Beachtung der erforderlichen Syntax exakt umgesetzt.
Die Ableitungen $u'$ und $v'$ entsprechen einem rekursiven Prozeduraufruf.
Hinweis:
Es ist zu beachten, dass bei allen höheren Funktionen innere Ableitungen existieren können, auf die die Kettenregel anzuwenden ist.
So gilt für die Ableitung der Prozedur sqr:
(list '* (list '* 2 term) (ableitung term var))
Aufgaben
-
Implementieren Sie alle weiteren Differentiationsregeln in dem oberen Definitionsfenster.
Informieren Sie sich ggf. im Tafelwerk.
Vereinfachen von Termen
Die ersten Testläufe zeigen sofort die Notwendigkeit, die symbolisch generierten Terme zu vereinfachen.
Dazu sei ebenfalls ein Programmfragment vorgegeben:
Auch hier wird zwischen unären und binären Operationen unterschieden.
Insbesondere elementare Vereinfachungen dürfen dabei nicht übersehen werden:
(vereinfache '(- term)) --> (- (vereinfache term)) (vereinfache '(+ term1 0)) --> (vereinfache term1) (vereinfache '(* 0 term2)) --> 0 (vereinfache '(* term1 1)) --> (vereinfache term1)
Sind die auftretenden Terme Zahlen, so wird natürlich gerechnet:
(if (and (number? term1) (number? term2)) (+ term1 term2))
Bei unären Operationen kann vereinfacht werden, wenn Operation und Umkehroperation nacheinander zum Einsatz kommen:
(vereinfache '(sqr (sqrt term))) --> (vereinfache term)
Ein nicht zu vereinfachender Ausdruck wird unverändert zurück gegeben.
Aufgaben
- Ergänzen Sie weitere Vereinfachungsregeln im entsprechenden Definitionsfenster.
- Begründen Sie, warum die Vereinfachung trotz der Implementation aller bekannter Vereinfachungsregeln nicht vollständig sein wird.
-
Zusatzaufgabe:
Entwickeln Sie die Prozedur vereinfache-vollstaendig:Quelltext überprüfen:
Numerische Auswertung symbolischer Ausdrücke
Leistungsstark ist unser CAS erst dann, wenn mit den symbolischen Ausdrücken auch gerechnet werden kann. Dazu müssen diese zu vollständigen λ-Ausdrücken erweitert werden. Anschließend ist die Evaluation mit eval möglich:
Nun können wir auch Funktionen mit beliebigen Ableitungen grafisch darstellen.
Dazu verwenden wir die bekannte Prozedur schaubild.
Die gewünschten Ableitungen müssen aber erst evaluiert und in einer Funktionsliste "gesammelt" werden.