Regionale Begabtenförderung Oberfranken Computeralgebra mit MuPAD Dr. Rainer Dietrich Clavius-Gymnasium Bamberg 20. Oktober 2005 Kapitel 1 Grundlegende Fähigkeiten von MuPAD MuPAD ist ein Computer-Algebra-System. Das bedeutet, dass seine Fähigkeiten weit über die eines herkömmlichen Taschenrechners hinaus gehen. Während dieser nur mit Zahlen rechnet (numerische Berechnungen), kann MuPAD auch mit Variablen umgehen (symbolische Berechnungen) und beispielsweise Terme vereinfachen oder Gleichungen lösen. Daneben besitzt MuPAD auch sehr weit reichende Möglichkeiten zur Erstellung von Graphiken. 1.1 Numerische Berechnungen Wir beginnen mit einfachen Zahlenrechnungen. Im Gegensatz zum Taschenrechner gibt MuPAD nicht automatisch Ergebnisse in Dezimaldarstellung an, sondern rechnet soweit wie nur möglich exakt. Dies schließt auch mathematische Konstanten wie π oder e ein, die deshalb nicht weiter vereinfacht werden. • 1/2+1/3 • sqrt(12) • (2*PI)ˆ2 Die Dezimaldarstellung kann auf zwei Arten erzwungen werden: Entweder mit dem Befehl float oder indem bereits die Eingabe als Dezimalbruch erfolgt. • float(5/6) 2 1.2. Symbolische Berechnungen 3 • sqrt(12.0) Die vorgegebene Einstellung sind dabei 10 gültige Ziffern; das lässt sich aber ändern. • DIGITS:=50 • float(1/7) • float(PI) Zum Schluß setzen wir die Anzahl der Dezimalstellen auf den Ursprungswert zurück. Der Wert von DIGITS ist dann also wieder 10. • delete DIGITS • float(1/7) • DIGITS 1.2 Symbolische Berechnungen MuPAD rechnet mit Variablen nach den üblichen Rechenregeln, wichtig (und leicht vergessen!) ist allerdings die explizite Angabe auch des Mal punktes“. ” • 5*a+3*a Natürlich kann MuPAD nicht erraten, in welcher Form wir einen Ausdruck gerne hätten; das müssen R. Dietrich, CG Bamberg 4 1. Grundlegende Fähigkeiten von MuPAD wir dem Programm mitteilen. • expand((a+b)ˆ2) Genauso wie sich Ausdrücke ausmultiplizieren lassen, können sie auch wieder faktorisiert werden. • factor(xˆ3-1) Aufgabe: Faktorisieren Sie und finden Sie die Nullstellen des Funktionsterms x4 − 4x2 + 3. Auch Summen (sogar unendliche) sind möglich. • sum(xˆk, k=0..10) Variablen kann man mit := einen Zahlenwert zuweisen; dabei ist der Doppelpunkt besonders wichtig. • a:=4 • 2*a Aufgabe: Was geschieht, wenn man im obigen Beispiel den Doppelpunkt vergisst? Mit := wird in MuPAD eine Zuordnung definiert. Es wird dabei zunächst die rechte Seite ausgerechnet und dieses Ergebnis dann unter dem Namen der linken Seite gespeichert. Der folgende Befehl ist also durchaus sinnvoll (und wird auch häufig verwendet): • a:=a+1 Hingegen macht die Gleichung a=a+1 keinen großen Sinn. Den Wert einer Variablen löscht man mit delete. Das Zurücksetzen der Stellenzahl oben bedeutet also lediglich, dass der (vom Benutzer eingegebene) Wert der internen Variablen DIGITS gelöscht wird (und durch den Standardwert ersetzt wird; das ist nötig, um kein Chaos entstehen zu lassen. Wie sollte sonst die nächste Ausgabe aussehen?) • delete a R. Dietrich, CG Bamberg 1.3. Funktionen 5 • 2*a MuPAD kann auch Gleichungen lösen. • solve(xˆ2=4) Wenn in der Gleichung mehrere Variable auftreten, muss man dem Programm mitteilen, nach welcher davon es auflösen soll. Bezüglich der anderen Variablen (den Parametern“) trifft MuPAD eine ” Fallunterscheidung. • solve(a*xˆ2+b*x+c=0, x) Als weiteres Beispiel betrachten wir die Nullstellen der Sinus-Funktion. • solve(sin(x)=0) Zusätzlich zum exakten Ergebnis ist hier die Mengenschreibweise auffällig: MuPAD gibt wirklich die komplette Lösungsmenge an! Zum Abschluß noch eine etwas kompliziertere quadratische Gleichung: • solve((3*xˆ2+9)/(xˆ2-1)-3=1/100) Aufgabe: In welchen Punkten schneidet der Graph zu f (x) = −5x3 + 8x2 − 3 die Koordinatenachsen? 1.3 Funktionen Funktionen werden in MuPAD am besten (mathematisch korrekt) als Zuordnungen definiert; für die Quadratfunktion schaut das z.B. so aus: R. Dietrich, CG Bamberg 6 1. Grundlegende Fähigkeiten von MuPAD • f:=x->xˆ2 Wichtig ist dabei wieder der Doppelpunkt; er zeigt MuPAD an, dass hier keine Gleichung steht (Lösungsmenge??), sondern eben eine Zuordnung. Neben den Potenzen kennt MuPAD noch viele weitere Funktionen, z.B. den Betrag abs(x) , die Quadratwurzel sqrt(x) , die gebräuchlichen trigonometrischen Funktionen sin(x), cos(x), tan(x) und andere mehr. In die Funktionsgleichung können verschiedene x-Werte eingesetzt werden. • f(2.5) Mit dem $-Befehl lässt sich eine Wertetabelle anlegen. • f(x) $ x=0..5 Wir können den Funktionsgraphen zeichnen lassen; dabei ist es sinnvoll, den Definitionsbereich auf der x-Achse vorzugeben. Der Befehl funktioniert allerdings auch ohne diese Vorgabe; MuPAD wählt dann selbstständig einen mehr oder weniger sinnvollen Bereich aus. • plotfunc2d(f(x), x=0..5) Man kann auch mehrere Funktionsgraphen gleichzeitig zeichnen. • plotfunc2d(sin(x), cos(x), x=-6..6) R. Dietrich, CG Bamberg 1.3. Funktionen 7 Aufgabe: Zeichnen Sie die Graphen von f (x) = −x + 2 sowie g(x) = −2x − 3 in ein Koordinatensystem und berechnen Sie ihren Schnittpunkt. Häufig begegnet man Funktionsgleichungen, die noch einen Parameter enthalten, wie z.B. • g:=x->k*xˆ2 Der Befehl subs ersetzt ( substituiert“) diesen Parameter durch eine Zahl; an der ursprünglichen ” Funktionsgleichung ändert sich dabei aber nichts. • subs(g(x), k=3) • g(x) Die entsprechende Funktionenschar lässt sich zeichnen, indem für g(x) zuerst eine Liste von Funktionen mit verschiedenen Parameterwerten erzeugt wird; diese Liste wird dann gezeichnet. • plotfunc2d((subs(g(x), k=j) $ j=-2..2), x=-3..3) R. Dietrich, CG Bamberg 8 1.4 1. Grundlegende Fähigkeiten von MuPAD Graphik Außer den gerade besprochenen Funktionsgraphen lassen sich auch weitere Graphiktypen erzeugen. Die meisten der spezielleren Befehle sind in sog. Bibliotheken“ enthalten, die von MuPAD erst bei ” Gebrauch geladen werden. Uns interessiert hier die Grafik-Bibliothek, erkennbar am Vorsatz plot:: . Zunächst betrachten wir Graphiken in Parameterdarstellung. Im ersten Beispiel nimmt x alle Werte t zwischen 0 und 2π an; y jeweils den Sinus davon. • plot(plot::Curve2d([t,sin(t)], t=0..2*PI)) Das ist natürlich einfach der Graph der Sinus-Funktion. Aber was passiert, wenn x selbst wieder, z.B., der Kosinus einer Variablen t ist? • plot(plot::Curve2d([cos(t),sin(t)], t=0..2*PI)) R. Dietrich, CG Bamberg 1.4. Graphik 9 Es ergibt sich ein Kreis, der allerdings auf den ersten Blick nicht danach ausschaut. Die nötige gleiche Skalierung der beiden Achsen lässt sich mit der Graphikoption Scaling=Constrained erreichen. • plot(plot::Curve2d([cos(t),sin(t)], t=0..2*PI),Scaling=Constrained) Mit etwas Spielerei kann man eine Menge interessanter Figuren erzeugen (der Fachausdruck dafür heißt Lissajous-Figuren). U.U. ist es sinnvoll, die Anzahl der Stützstellen, an denen die Funktionen berechnet werden, mit der Graphikoption Grid=[...] zu erhöhen; die gezeichnete Kurve wird dadurch glatter“. ” • plot(plot::Curve2d([cos(3*t),sin(7*t)], t=0..2*PI, Grid=[300]), Scaling=Constrained) R. Dietrich, CG Bamberg 10 1. Grundlegende Fähigkeiten von MuPAD Häufig braucht man auch eine graphische Darstellung von Datenpunkten, beispielsweise aus einer Wertetabelle oder aus einem physikalischen Experiment. Dazu wird eine Liste von x- und yKoordinaten mit plot::Pointlist in eine druckbare“ Form gebracht und dann geplottet. ” • plot(plot::Pointlist([x,sin(3.9*x)] $ x=0..20)) Will man die Punkte verbinden, so braucht man die Option DrawMode=Connected. • plot(plot::Pointlist([x,sin(3.9*x)] $ x=0..20,DrawMode=Connected)) R. Dietrich, CG Bamberg 1.4. Graphik 11 R. Dietrich, CG Bamberg Kapitel 2 Programmieren mit MuPAD Bis jetzt hatten wir für jeden Befehl eine neue Zeile begonnen. Es lassen sich aber auch mehrere Rechnungen in eine Zeile schreiben, dann muss jede mit einem Semikolon ;“ abgeschlossen werden. ” Soll eine Rechnung zwar durchgeführt werden, die Ausgabe aber nicht erscheinen, so muss ein Doppelpunkt :“ stehen. Eine Eingabe darf sich auch über mehrere Zeilen erstrecken; auf diese Weise ” entsteht bereits eine einfache Programmstruktur. • 2+3; 3+4: 4+5; 5+6; 6+7 2.1 Schleifen Die einfachste Form einer Schleife lässt sich mit dem for-Befehl realisieren. Das folgende Programm druckt eine Liste der Quadrate der Zahlen von 1 bis 4 aus. • for i from 1 to 4 do print(iˆ2) end_for: 1 4 9 16 Dabei kann auch rückwärts gezählt oder die Schrittweite verändert werden. • for i from 20 downto 2 step 4 do print(iˆ2) end_for: 400 256 12 2.1. Schleifen 13 144 64 16 Der oben bereits für die Wertetabelle verwendete $-Befehl stellt in vielen Fällen eine elegante Alternative zu einer for-Schleife dar, z.B. • iˆ2 $ i=1..4 Dabei kann die Zählvariable auch aus einer beliebigen Liste gewählt werden. • iˆ2 $ i in [2,4,6,8,10] Als weiteres Beispiel betrachten wir die Fakultät einer natürlichen Zahl n. Man versteht darunter das Produkt aller natürlichen Zahlen kleiner oder gleich n und schreibt dafür n!. Zur Berechnung können wir eine for-Schleife benutzen, wie im folgenden Beispiel für 5!: • n:=5: x:=1: for i from 1 to n do x:=x*i end_for Flexibler wird unser Programm, wenn wir ihm einen Namen geben, unter dem es später wieder aufgerufen werden kann. Das leistet der Befehl proc. Zunächst wird die Variable angegeben, die eingegeben werden muss, bei uns also n. Dann kommen sog. lokale Variable, d.h. solche, die nur innerhalb des Programms verwendet werden, wie der Zählindex i. Das eigentliche Programm befindet sich zwischen den Befehlen begin und end proc . • fakultaet:=proc(n) local i; begin x:=1: for i from 1 to n do x:=x*i end_for end_proc: • fakultaet(5) Als weitere Anwendung wollen wir die Quadratwurzel einer Zahl x von Hand“ berechnen. Eine der ” ältesten bekannten Methoden, das Heron-Verfahren oder Babylonische Wurzelziehen“ lässt sich sehr ” schön geometrisch veranschaulichen: Endziel ist ein Quadrat mit Flächeninhalt x und Seitenlängen √ x. Ausgehend von einem beliebigen Rechteck mit Seitenlängen a und xa (also mit richtigem“ ” R. Dietrich, CG Bamberg 14 2. Programmieren mit MuPAD Flächeninhalt) kommen wir dem Quadrat näher, wenn die neue Rechteckseite das (arithmetische) Mittel der beiden alten ist: aneu = (a + xa )/2. Natürlich müsste noch gezeigt werden, dass sich dieses Verfahren tatsächlich auf ein Quadrat zubewegt, d.h. konvergiert. Um unseren Erfolg zu kontrollieren setzen wir zunächst die Anzahl der angezeigten Stellen hoch, z.B. auf 30. • DIGITS:=30 Gemäß dem Heron-Algorithmus rechnen wir eine feste Anzahl von Schritten durch. Das Ergebnis unseres Programms vergleichen wir anschließend mit dem exakten Wert, wie er z.B. vom MuPADBefehl sqrt geliefert wird. • wurzel:=proc(x) local a,aneu,i; begin a:=1; for i from 1 to 4 do aneu:=(a+x/a)/2.0; a:=aneu end_for end_proc: • wurzel(12) • sqrt(12.0) Für vier Schritte schon nicht schlecht. Um genauer zu werden, muss offensichtlich die Anzahl der Schritte erhöht werden. Allerdings sollte man auch nicht mehr Schritte ausführen, als gebraucht werden um das Ergebnis garantiert bis auf z.B. 6 Nachkommastellen genau anzugeben. Dabei hilft die while-Schleife. Sie prüft zunächst, ob die geforderte Bedingung erfüllt ist. Solange das der Fall ist, führt sie die dann folgenden Befehle aus, z.B. • y:=13: while y>=10 do print(y,yˆ2); y:=y-1 end_while: 13, 169 12, 144 11, 121 10, 100 Ersetzen wir also im obigen Programm wurzel die for-Schleife durch eine while-Schleife. Dabei R. Dietrich, CG Bamberg 2.2. Verzweigungen 15 brauchen wir eine neue Variable d, die den Unterschied zwischen a und aneu misst (am besten nimmt man den Betrag abs(a-aneu) ). Das Programm soll abbrechen, wenn d einen vorgegebenen Wert unterschreitet. • wurzel:=proc(x) local a,aneu,d; begin a:=1; d:=1; while d>=10ˆ-6 do aneu:=0.5*(a+x/a); d:=abs(a-aneu); a:=aneu end_while end_proc: • wurzel(12) • sqrt(12.0) Aufgabe: Lassen Sie sich vom Programm nicht nur das Endergebnis, sondern alle Zwischenwerte von aneu ausgeben. 2.2 Verzweigungen Häufig soll ein Programm unterschiedliche Befehle ausführen, je nachdem ob eine bestimmte Bedingung erfüllt ist oder nicht. Mit dem if-Befehl lassen sich solche Verzweigungen realisieren. Als Beispiel dazu ein einfaches Ratespiel: Ein Spieler gibt eine natürliche Zahl zwischen 1 und 1000 ein, der andere schaut weg. • x:=721 Diese Zeilen müssen natürlich wieder gelöscht werden, aber MuPAD merkt“ sich die Zahl. Der ” andere Spieler ruft dann das folgende Programm auf, bis er die Zahl erraten hat. R. Dietrich, CG Bamberg 16 2. Programmieren mit MuPAD • raten:=proc(y) begin if y<x then print("zu klein") else print("zu groß oder richtig") end_if end_proc: • raten(800) “zu groß oder richtig“ Natürlich bräuchte man eigentlich drei Alternativen: zu groß, zu klein und genau richtig. Das lässt sich erreichen, indem man die Verzweigungen ineinander schachtelt. • raten:=proc(y) begin if y=x then print("richtig") else if y<x then print("zu klein") else print("zu groß") end_if end_if end_proc: • raten(721) “richtig“ Verzweigungen sind auch oft nützlich, um unerwünschte Eingaben zu verhindern. So macht (zumindest nach unserer Definition) die Fakultätsfunktion n! nur für natürliche Zahlen n Sinn. Ergänzen wir daher unser Programm aus dem letzten Abschnitt durch eine entsprechende Abfrage. Der Test kann mit einem Befehl wie is(4, Type::PosInt) durchgeführt werden. Die Antwort ist entweder TRUE“, falls die Zahl nach der Klammer wie hier tatsächlich eine ” natürliche Zahl ist, oder FALSE“ falls nicht. ” • fakultaet:=proc(n) local i; begin if is(n, Type::PosInt) then x:=1: for i from 1 to n do x:=x*i end_for else print("falsche Eingabe") end_if end_proc: • fakultaet(1.8) “falsche Eingabe“ R. Dietrich, CG Bamberg 2.3. Listen 17 Aufgabe: Ergänzen Sie das Programm zum Heron-Algorithmus um eine Abfrage, die verhindert, dass die Wurzel aus einer negativen Zahl gezogen wird. 2.3 Listen Ein großer Vorteil von MuPAD liegt im Umgang mit komplexeren Datenstrukturen. Davon steht eine Vielzahl zur Verfügung; wir beschränken uns hier auf den Umgang mit Listen und Matrizen. Unter einer Liste versteht man eine geordnete Folge beliebiger Objekte; sie ist in eckigen Klammern eingeschlossen. Am einfachsten lässt sich eine Liste durch Aufzählen angeben • liste:=[1,6,"a",89.7] oder mit dem Folgenoperator $ erzeugen. • quadrate:=[xˆ2 $ x=0..10] Der Zugriff geschieht über den Index, d.h. die Nummer in der Liste. Wichtig dabei ist, dass die Zählung immer mit 1 beginnt, auch wenn die Liste anders erzeugt wurde (siehe oben bei den Quadratzahlen). • liste[4] • quadrate[5] Die Listenelemente selbst (d.h. ohne die umschließende Klammer) nennt man Operanden; man erhält sie mit dem Befehl op . Auf diese Weise lassen sich ebenfalls einzelne Listenelemente anzeigen. • op(liste) • op(quadrate,5) Folgerichtig ist die Anzahl der Listenelemente die number of operands“ nops : ” • nops(quadrate) R. Dietrich, CG Bamberg 18 2. Programmieren mit MuPAD Listen lassen sich beliebig verschachteln; die entsprechende Indizierung läuft von außen nach innen. Bei der Schreibweise unterscheiden sich die oben bereits vorgeführten Möglichkeiten geringfügig. • doppelliste:=[["a","b","c"],["d","e","f"],["g","h","i"]] • doppelliste[2][3]; op(doppelliste,[2,3]) Listen lassen sich um einzelne Elemente erweitern oder verkürzen. • append(liste,"neu") • delete(liste[2]); liste Beide Befehle funktionieren etwas unterschiedlich: Während delete tatsächlich die Liste verändert (das Ergebnis selbst aber nicht darstellt) erzeugt append eine neue, noch unbenannte Liste. Diese könnte dann natürlich durch die Zuordnung liste:= wieder unter dem alten Namen gespeichert werden. Auch das Suchen nach bestimmten Elementen ist möglich. • contains(liste,"a") Die Ausgabe ist die Position, an der das gesuchte Element zum ersten Mal auftritt; ist es überhaupt nicht vorhanden, gibt MuPAD die Zahl 0 zurück. Im Zusammenhang mit graphischen Darstellungen hatten wir im letzten Kapitel bereits mit Listen gearbeitet. Jetzt können wir das Ganze in ein etwas anspruchsvolleres Beispiel einbetten: Wir schreiben die Zwischenwerte aus dem Programm zur Wurzelbestimmung mit append in eine Liste und stellen sie graphisch dar. (Zur Erinnerung: Das geht mit plot(plot::Polygon2d()) . R. Dietrich, CG Bamberg 2.4. Rechnen mit Listen 19 • wurzel:=proc(x) local a,aneu,d,l; begin a:=1; l:=[]; d:=1; while d>=10ˆ-6 do aneu:=0.5*(a+x/a); l:=append(l,aneu); d:=abs(a-aneu); a:=aneu end_while; plot(plot::Pointlist([i,l[i]] $ i=1..nops(l),DrawMode=Connected)); a end_proc: • wurzel(500) 2.4 Rechnen mit Listen Sollen auf Listen Rechenoperationen angewendet werden, so ist dies grundsätzlich mit (evtl. mehrfachen) for-Schleifen möglich. Z.B. lässt sich eine Verdreifachung unserer Quadratzahl-Liste folgendermaßen erreichen: • dreifach:=[0 $ i=1..nops(quadrate)]: for i from 1 to nops(quadrate) do dreifach[i]:=3*quadrate[i] end_for: dreifach Es gibt jedoch in MuPAD eine Reihe spezieller Operationen für Listen, die häufig wesentlich eleganter zu programmieren sind. map wendet eine Funktion (definiert durch den Zuordnungspfeil) der Reihe R. Dietrich, CG Bamberg 20 2. Programmieren mit MuPAD nach auf jedes Element einer Liste an: • dreifach:=map(quadrate,x->3*x) Sollen hingegen die Elemente einer Liste miteinander verknüpft werden, so kann die entsprechende Rechenanweisung vorangestellt werden. Die Summe aller unserer Quadratzahlen erhält man so ganz schnell mit • _plus(op(quadrate)) Entsprechendes funktioniert auch mit mult für die Multiplikation aller Listenelemente. Dass es Elemente sind, die miteinander verknüpft werden, wird durch den eingeschalteten op-Befehl berücksichtigt. Ohne ihn erhält man eine Fehlermeldung. Die Verknüpfung von zwei Listen miteinander ist dagegen ohne Weiteres möglich; gemeint ist dabei die paarweise Verknüpfung von Elementen jeder der beiden Listen. Das Verfahren erinnert an einen Reissverschluß und trägt daher den Namen zip. Für die Addition beispielsweise sieht das so aus: • zip(quadrate,dreifach,_plus) Falls die beiden Listen verschieden lang sind, stoppt der Reissverschluß mit dem Ende der kürzeren Liste. Die zuletzt vorgestellten Konstruktionen sind zunächst wohl etwas ungewohnt, stellen sich aber bald als sehr mächtig heraus. Insbesondere helfen sie, den Programmieraufwand zu reduzieren und die Programme dadurch übersichtlicher zu halten. 2.5 Matrizen Der Datentyp der Matrix schließt sich sehr eng an verschachtelte Listen an, als Elemente dürfen allerdings nur Zahlen vorkommen. • mat1:=matrix([[1,2,3],[4,5,6],[7,8,9]]) • mat2:=matrix([[2,0,0],[0,2,0],[0,0,2]]) R. Dietrich, CG Bamberg 2.5. Matrizen 21 • matrix(quadrate) Die einzelnen Einträge werden mit Zeilen- und Spaltennummer angesprochen; die Schreibweise ist etwas anders als bei verschachtelten Listen. • mat1[2,3] Matrizen können direkt (komponentenweise) addiert, multipliziert (im Sinne einer Matrix-Multiplikation) oder potenziert werden: • mat1+mat2; mat1*mat2; mat2ˆ2 Aufgabe: Führen Sie die obige Addition der Listen quadrate und dreifach als Additon geeigneter Matrizen aus. R. Dietrich, CG Bamberg