C/C++ symbolische Mathematik beibringen...
-
Ich kann die 2 Stündchen leider nicht opfern, sonst würde ich das implementieren
.
-
ths schrieb:
Also im Prinzip hast du ja Recht: "Nur" analysieren und bewerten. Aber
das wird richtig heftig... wie man in dem Dokument sieht.Von der Mathematik her ist es ja nicht so heftig. Ausserdem arbeitet das Programm nur dumm irgendwelche Möglichkeiten ab. Die Hauptschwierigkeit besteht imho darin, die einzelnen Gesetze in eine Form zu bringen, die das Programm versteht und darin, den Term zu bewerten.
-
MaSTaH schrieb:
Ich kann die 2 Stündchen leider nicht opfern, sonst würde ich das implementieren
.
der suchbaum ist nicht das ding. der sucher auch nicht. heuristiken wachsen mit der beschäftigung am problem und sind anfangs nichtmal relevant.
die transformationsregeln sind ein ding. die sauber hinschreiben zu können kannste in c++/java verbacken. da muss ne neue sprache her (oder lisp, wie immer).
wir brauchen allein mehr als ne woche, um nen mathematiker einzufangen, der uns sinnvolle regeln erzählt. eventuell ist es sogar für jeden nichtmathematiker unmöglich nen mathematiker einzufangen. fatales problem.
-
Das war auch nicht ganz ernst gemeint. Ich dachte das war klar
.
-
Ein paar Links zum Thema:
(CAS == Cumputer Algebra System)
Erklärung, wie man ein CAS erstellt (allgemein):
http://www.math.wpi.edu/IQP/BVCalcHist/calctoc.htmlEin kostenloses CAS, mit Quellcode in C++:
http://yacas.sourceforge.net/Nochmal kostenloses CAS, mit Quellcode:
http://maxima.sourceforge.net/index.shtmlFür alle, die Lisp und Prolog noch nicht kennen:
http://de.wikipedia.org/wiki/PROLOG
http://de.wikipedia.org/wiki/Lisp(PROLOG ist ja echt der Hammer -- werd mich mal mit beiden beschäftigen)
-
Oh -- was interessantes:
Ladet euch mal das Prolog-Arbeitsbuch runter:
http://www.bildung-mv.de/download/fortbildungsmaterial/arbeitsbuch_prolog.pdfAuf Seite 68 wird erklärt, wie man Prolog das Differenzieren (symbolisch!!!)
beibringt!Also GENAU das, was wir suchen. Ist doch schonmal was. Wie kann ich aber
Prolog in eine grafische C / C++ / Java - Oberfläche einbinden?
-
Symbolisches Differenzieren ist trivial.
-
mfdg schrieb:
Symbolisches Differenzieren ist trivial.
Symbolisches integrieren auch
Und alles andere auch... für einen
Menschen. Jedes Problem für sich ist doch kein ProblemNur alles jederzeit
zu ermöglichen in einem Programm ... das ist doch das Problem
-
ths schrieb:
mfdg schrieb:
Symbolisches Differenzieren ist trivial.
Symbolisches integrieren auch
Und alles andere auch... für einen
Menschen. Jedes Problem für sich ist doch kein ProblemNur alles jederzeit
zu ermöglichen in einem Programm ... das ist doch das Problemwar eher anders gemeint, nehme ich an. in zwei oder drei bildschirmen voll mit lisp baut man nen symbolischen ableiter. als übung für anfänger findet man den in büchern.
symbolischen integrieren hingegen ist gemein. so gemein, daß ich noch kein computeralebrasystem gesehen habe, das mich in dieser hinsicht befriedigte. mußte oft in büchern nachgucken und rumprobieren, bis ich ne umformung gefunden hatte, von der ausgegend das prog es schaffte. zum glück ist aber differenzieren trivial, und darum konnte ich mit dem prog immer die probe machen.
-
Ist das sooo gemein zu programmieren?
Ich habs noch nicht
probiertIch werde es mal mit der Prädikatenlogik mit Prolog
probieren ... 1,5 Wochen Semester-Ferien hab ich ja noch
-
ths schrieb:
Ist das sooo gemein zu programmieren?
Ich habs noch nicht
probiertIch werde es mal mit der Prädikatenlogik mit Prolog
probieren ... 1,5 Wochen Semester-Ferien hab ich ja nochbeim differenzieren hat man halt 5 regeln und für sin(x), cos(x) exp(x) und noch ein paar ne direkte formel und fertig.
beim integrieren stehen ja bereits in der formelsammlung 30 seiten mit wissenswerten integralen. und das fiese ist, daß selbst nach reinklopfen der 30 seiten der rechner noch relativ blind ist (aber endlich anständig lahm).
beim differenzieren kann man immer direkt die ableitung hinschreiben. eventuell müssen nachher unterterme weiter abgeleitet werden.
beim integrieren kann man evtl erst ne formel benutzen, nachdem! man 5 zauberhafte umformungen getan hat. udn die methoden, die der mensch benutzt, erscheinen so willkürlich und vage. "mal was substituieren". jo, soll der rechner doch alle möglichen teilterme mal substituieren und inegration durch substitution probieren. er hat ja keinen blick dafür, zu sehen, was vielversprechende substitutionsopfer sind.
also mir erscheint es nicht einfach.
-
Ich arbeite gerade das Differential-Beispiel von Prolog durch. Immerhin
hab ich noch nie was mit Prolog gemacht. Wenn ich es dann kapiert habe,
wie Prolog das macht, werde ich die Integration anfagenMal sehen,
wo ich auf Probleme stoße...Aber substituieren und partielle Integration... könnten mit Prolog
schwer werden. Da braucht man dann, die schon öfter erwähnte, Heuristik
-
volkard schrieb:
beim integrieren kann man evtl erst ne formel benutzen, nachdem! man 5 zauberhafte umformungen getan hat. udn die methoden, die der mensch benutzt, erscheinen so willkürlich und vage. "mal was substituieren". jo, soll der rechner doch alle möglichen teilterme mal substituieren und inegration durch substitution probieren. er hat ja keinen blick dafür, zu sehen, was vielversprechende substitutionsopfer sind.
Ich frage mich, ob man beim symbolischen Intergrieren vielleicht besser mit Potenzreihen arbeiten könnte.
...also in der Art:
1. Funktion in Potenzreihe entwickeln bzw. Potenzreihen der "Teilfunktionen" betrachten und gesamt vereinfachen.
2. Potenzreihe integrieren.
3. Potenzreihe wieder in bekannte Funktionen "zerlegen"....nur so ne undurchdachte Idee.
-
...nach etwas drüber nachdenken -> dumme Idee.
-
Das ist echt blöd... es gibt kein Prolog-Forum
Also das Differenzieren geht ohne Probleme -- sehr beeindruckend.
Auch das automatische Vereinfachen von Termen geht. Was nicht geht,
ist beides gleichzeitigDafür brauch ich mal nen Prolog-Experten
Differenzieren in 16 Zeilen (!!!) :
d(X,X,1). d(C,X,0) :- atomic(C), C \= X. d(pot(U,N),X,N*pot(U,N1)*DU) :- N>1, N1 is N-1, d(U,X,DU). d(pot(U,N),X,N*pot(U,N1)*DU) :- N<0, N1 is N-1, d(U,X,DU). d(sin(U),X,cos(U)*DU) :- d(U,X,DU). d(cos(U),X,0-sin(U)*DU) :- d(U,X,DU). d(exp(U),X,exp(U)*DU) :- d(U,X,DU). d(log(U),X,(1/X)*DU) :- d(U,X,DU). d(U+C,X,DU) :- atomic(C), C \= X, d(U,X,DU), !. d(C+U,X,DU) :- atomic(C), C \= X, d(U,X,DU), !. d(F+G,X,DF+DG) :- d(F,X,DF), d(G,X,DG). d(U-C,X,DU) :- atomic(C), C \= X, d(U,X,DU), !. d(C-U,X,0-DU) :- atomic(C), C \= X, d(U,X,DU), !. d(F-G,X,DF-DG) :- d(F,X,DF), d(G,X,DG). d(F*G,X,DF*G+F*DG) :- d(F,X,DF), d(G,X,DG). d(F/G,X,(G*DF-F*DG)/(G*G)) :- d(F,X,DF), d(G,X,DG).
Das Vereinfachen der Terme ist etwas länglicher:
vereinfacht(T,T) :- atomic(T). vereinfacht(T,V) :- T=L*R, vereinfacht(L,L1), vereinfacht(R,R1), m(L1*R1,V). vereinfacht(T,V) :- T=L+R, vereinfacht(L,L1), vereinfacht(R,R1), a(L1+R1,V). vereinfacht(T,V) :- T=L-R, vereinfacht(L,L1), vereinfacht(R,R1), s(L1-R1,V). a(X+0,X) :- !. a(0+X,X) :- !. a(X+Y,Z) :- integer(X), integer(Y), Z is X+Y. a(Y+X+W,V) :- integer(Y), integer(W), a(Y+W,Z), a(Z+X,V). a(X+Y+W,V) :- integer(Y), integer(W), a(Y+W,Z), a(Z+X,V). a(Y+X+W,V) :- a(Y+W,Z), Z \= Y+W, a(Z+X,V), !. a(X+Y+W,V) :- a(Y+W,Z), Z \= Y+W, a(Z+X,V), !. a(X+X,V) :- m(2 * X,V), !. a(Y*X+X,V) :- a(Y+1,W), m(W*X,V), !. a(X+Y*X,V) :- a(Y+1,W), m(W*X,V), !. a(Y*X+Z*X,V) :- a(Y+Z,W), m(W*X,V), !. a(X+Y,Y+X) :- integer(Y), !. a(X+Y,X+Y). /* Default-Klausel */ s(X-0,X) :- !. s(X-Y,Z) :- integer(X), integer(Y), Z is X-Y, !. s(X-X,0) :- !. s(X-Y-W,V) :- integer(Y), integer(W), a(Y+W,Z), s(X-Z,V), !. s(Y-X-W,V) :- integer(Y), integer(W), s(Y-W,Z), s(Z-X,V), !. s(X-Y-W,V) :- s(Y+W,Z), Z \= Y+W, s(X-Z,V), !. s(Y-X-W,V) :- s(Y-W,Z), Z \= Y-W, s(Z-X,V), !. s(Y+X-W,V) :- integer(Y), integer(W), s(Y-W,Z), a(Z+X,V), !. s(Y+X-W,V) :- s(Y-W,Z), Z \= Y-W, a(X+Z,V), !. s(X+Y-W,V) :- s(Y-W,Z), Z \= Y-W, a(X+Z,V), !. s(Y*X-X,V) :- s(Y-1,W), m(W*X,V), !. s(X-Y*X,V) :- s(1-Y,W), m(W*X,V), !. s(Y*X-Z*X,V) :- s(Y-Z,W), m(W*X,V), !. s(X-Y,X-Y). /* Default-Klausel */ m(0*X,0) :- !. m(X*0,0) :- !. m(1*X,X) :- !. m(X*1,X) :- !. m(X*Y,Z) :- integer(X), integer(Y), Z is X*Y, !. m(X*Y*W,V) :- integer(Y), integer(W), m(Y*W,Z), m(Z*X,V), !. m(Y*X*W,V) :- integer(Y), integer(W), m(Y*W,Z), m(Z*X,V), !. m(X*Z,Z*X) :- integer(Z), !. m(X*Y,X*Y) :- !. /* Default-Klausel */
Das Problem ist nun, dass "der Vereinfacher" sin(U), cos(U) etc. nicht
als Atom anerkennt. Ist auch richtig. Wenn man jetzt statt sin(U)
'sin(U)' schreiben würde, wäre sin(U) auch ein Atom -- es würde gehen.
Aber dann kann man sin(U) nicht mehr differenzierenMan muss also die erste Zeile "des Vereinfachers" ändern. Wenn ich
vereinfacht(T,T) :- atomic(T) | compound(T). nehme, geht es.
Nur auf compound(T) trifft alles zu -- auch die zu vereinfachen
Terme :p Geht also auch nicht.Wie auch immer...
==================================
Ich habe mal drüber nachgedacht, wies mit der Integration wäre:
Integration durch Substitution und partielle Integration wäre
kein Problem...Substitution
Zwei Möglichkeiten:- Man kann ihn immer substituieren lassen. Mein Gott: Dann substituiert
er halt auch x zu x im einfachen Fall! Davon geht die Welt nicht unter.
Beim differenzieren wird auch immer die Kettenregel benutzt, auch wenn es
nicht nötig wäre --> auch kein Problem. - Oder man überprüft einfach mit atomic() oder var() ob
in der Funktion eine weitere steht, wie:
sin(x) --> atomic(X) ist true --> nicht substituieren
sin(x^2) (heisst im Code: sin(pot(x,2))) --> atomic ist false --> substituieren... etc.
Die partielle Integration an sich ist ja kein Problem: Differenzieren
kann er ja schon -- das ist eine VoraussetzungMan muss sich eine Bedingung
ausdenken, ab wann er versuchen soll es partiell zu machen... Sollte nicht
das Problem sein.Integrieren, elementar:
Kann er schon, ähnlich, wie es Jester vorher gesagt hatteIst ja auch
logisch: Die einfache Integration ist nur die Umkehrung der Differentiation.
Gibt man dem Programm die Ableitung vor, und lässt ihm die Aufleitung suchen,
so findet er sie ab und zu -- nicht immerAlso könnte man schon das erste Prädikat für die Integration aus dem
Stehgreif hinschreiben:integral(I,X,V) := d(V,X,I).
Das ist der Grundstein. Dann muss man halt die ganzen Besonderheiten
berücksichtigen...Aber erstmal will ich, dass der Vereinfacher mit dem Differenzierer zusammen
arbeitetErst dann kann man auf dem Gesamtergebnis den Integrierer
definieren...Gibt es hier einen Prolog-Expertern
- Man kann ihn immer substituieren lassen. Mein Gott: Dann substituiert
-
Nachtrag (der andere Beitrag war schon zu lang):
Es gibt für Prolog ein C++- und Java-Interface
Wir können dann also eine GUI basteln, und eine
beliebige Eingabe an Prolog weiterleiten...Dann catch man die Ausgabe von Prolog und gibt
sie in der GUI wieder aus. Wenn der User noch
ein numerisches Ergebnis möchte, oder ein grafisches
Ergebnis, dann wird die Ausgabe noch geparst und
man setzt für die Variablen Werte ein ... bla bla.Die Ausgabe könnte man in Java mit einem StringTokenizer
auseinander nehmen...
-
haste wo ein kleines prolog zum downloaden? dann probiere ich's mal.
-
Ja, SWI-Prolog ist kostenlos.
http://www.swi-prolog.org/Einfach installieren, und die Quellcodes als .pro oder .pl Datei
in deinen Prolog-Ordner packen. Bei mir wurde der Prolog-Ordner
auf dem Desktop erzeugt. Dort kommen nur Quellcodes hinein.Das Programm wird natürlich schön sauber im Order Programme
installiert, klar.Nach dem Start von Prolog musst du jede Quellcode-Datei einzel
mit consult(DATEINAME). laden. Aber ohne die Dateiendung!
Und immer den Punkt mitschreiben!Differenzieren kannst du mit:
d(FUNKTION,Variable,Ziel). <-- Punkt nicht vergessen!"Funktion" ist klar. x^y geht mit pot(x,y) --> x^2 => pot(x,2)
cos(..), sin(..), exp(..) .... etc. ist klar. In der Funktion
alles klein schreiben!"Variable" ist die zu differenzierende Variable, __kleingeschrieben__!
Also z. B.: "x".Und Ziel ist eine __großgeschriebene__ Variable. Name ist egal.
Beispiel:
d(pot(x,2),x,V).
d((sin(pot(x,2))*pot(x,5))/cos(x),x,V).Vereinfachen kannst du mit:
vereinfacht(ALT,NEU).Beispiel:
vereinfach(0*x+5,V).Viel Spaß. Danke das du es auch probierst
Ich werde mich jetzt ins
Usenet begeben -- dort gibt es Prolog-Bereiche
-
zwischenstand:
thx. SWI prolog ist praktisch.
mit dem code bin ich aber unzufrieden.
d(C,X,0) :- atomic(C), C \= X.
kann eigentlich nicht sein. hier ist doch gemeint, C ist von X unabhängig. dafür muß ne klausel gebaut werden./*C ist konstant bezüglich X also C ist nicht abhängig von X genaue implemetierung fehlt noch*/ const(C,X) :- atomic(C), C \= X.
und der rest vom code ist so kompliziert, daß ich alles verwerfe und neu anfange. vielleicht kapiere ich ja dann meinen eigenen.
ich fürchte, er wird sogar recht einfach. muss jetzt aber erstmal pizza und bier einkaufen und vertilen.
-
Ich habe gestern Abend und gestern Nacht diesen Schritt gewagt, und alles
neu gemachtDu kannst dir diesen Schritt, nach der Pizza, sparen. Ich werde es dir
erklärenDer Code ist total einfach
Wichtig:
- a) Vergiss mal Java, C++ etc. Das hier ist auf einer ganz anderen Ebene!
- b) Wir teilen dem Programm nur Fakten mit. Alles andere, wie weitere
Rückschlüße, zieht das Programm alleine. Wir müssen nicht jede Kombination
von Regel(n) niederschreiben.
Code:
d(X,X,1).Bedeutet, dass wenn du ein beliebiges X (ist nicht die Variable x, sondern
irgendwas), nach X ableiten willst, kommt immer 1 heraus.Also x nach x ableiten, etc.
Code:
d(C,X,0) :- atomic(C), C \= X.C ist deine gegebene Gleichung, X deine gewünschte Variable. Wenn die
Gleichung nur eine Konstante enthält (das prüft man mit atomic()), und
C ungleich X ist, ist dass Ergebnis 0. Ungleichheit wird also mit \=
bezeichnet.Code:
d(pot(U,N),X,N*pot(U,N1)*DU) :- N>1, N1 is N-1, d(U,X,DU).pot(U,N) soll U^N sein. Wenn wir also U^N ableiten wollen, über X, so lautet
das Ergebnis N*pot(U,N1)*DU ... WENN:
a) N>1
b) N-1 berechnet, und N1 zugewiesen wurde
c) und d(U,X,DU). erfolgreich verlauffen ist. Diese Regel arbeitet also Rekrusiv.Das "," ist die UND-Verknüpfung!!! Mit ":-" definiert man die Bedingungen,
wann die Regel auf der linken Seite gültig ist.Code:
d(sin(U),X,cos(U)*DU) :- d(U,X,DU).Wieder das Selbe. Das Ergebnis von sin(U) (hier ist gleich die Kettenregel
dabei!) über X abgeleitet, ist cos(U)*DU, WENN:
Die Ableitung (Rekrusiv) von d(U,X,DU). erfolgreich verlief.Damit kann man das ganze Differential-Programm verstehen. Eine Sache noch:
Das "!" ist eine Abbruchbedingt, wird Cut genannt. Damit wird der Vorgang
abgebrochen. Damit das Programm nicht noch eine andere Regel auf das fertige
Ergebnis anwendet.Ach ja: Prolog kennt kein negativen Operator... also -5 geht nicht! Man
muss leider 0 - 5 schreiben. Leider geht also auch 5 * (-1) nicht.Für das Vereinfachen mache ich einen neuen Beitrag.
Nochmal zur Klarheit:
Wir brauchen dem Programm nicht beibringen, wie man eine Konstante ableitet,
die unter einem Bruchstrich steht etc. Denn diese Aussage fehlt im Code,
wie viele andere auch. Aber das Programm leitet sich das selbst her. Es hat
eine gewisse "KI" wenn man so will.Es weiß ja, dass gilt:
d(C,X,0) :- atomic(C), C \= X.
... und diese Regel wird es nun in allen anderen Kombinationen selber anwenden.