%htmlDTD; ]> Automatische Berechnung von Grenzwerten und Implementierung in Mathematica Zurück - Inhalt - Übersicht - Vorwärts

6 Praxistests

Nachdem wir nun genug Algorithmen und Quelltexte studiert haben, wird es Zeit, das Ganze einmal im praktischen Einsatz zu beobachten. Schauen wir, wie weit der Algorithmus tragfähig ist und wie oft die Kompromisse der Implementierung uns dennoch scheitern lassen.

Soweit nicht anders vermerkt, kam für die Tests Mathematica 5.0 auf einem handelsüblichen 2.4GHz-PC zum Einsatz.

6.1 Einfache Auslöschung

Beginnen wir mit einem Beispiel aus
[Gru96], Beispiel 3.21 auf Seite 47. Die Funktion lautet:
In[1]:=
f = Hold [ ( E ^ H E ^ ( - x / ( 1 + H ) ) E ^ E ^ ( - x + H ) ) / ( H ^ 2 ) - E ^ x + x ] ;
In[2]:=
f = ReleaseHold [ f /. Power power ]
Out[2]=
x - x + H H - x - x 1 + H H 2
Schon um die Funktion unverändert eingeben zu können, muss man etwas tricksen, sonst schlägt die automatische Vereinfachung von Mathematica zu. Durch das Kapseln in Hold bleibt die Struktur solange erhalten, bis Mathematicas eigene Power-Funktion durch die interne Darstellung von MrvLimit ersetzt ist. Danach kann der Haltezustand aufgehoben werden. Optisch sieht die Ausgabe wie eine normale Funktion aus, doch das täuscht: Mathematica selbst kann mit dieser Funktion nicht viel anfangen. Es bleibt, noch H zu erklären:
In[3]:=
h = E ^ ( - x / ( 1 + E ^ - x ) )
Out[3]=
- x 1 + - x
In[4]:=
f = f /. H h
Out[4]=
x - x + - x 1 + - x - x 1 + - x - x - x 1 + - x 1 + - x ( - x 1 + - x ) 2


Für bessere Lesbarkeit wird der Debugausgabe beigebracht, den h-Term symbolisch darzustellen:
In[5]:=
OutputProcessor = ( ( # /. { ToInternal [ h , x ] H } ) & ) ; Damit ist der Weg frei für die Berechnung des Grenzwertes für x :
In[6]:=
MrvLimit [ f , x , Debug 1 ]
Enter Level 1 Call MrvLimitInf [ x - x + - x + H H - x 1 + H H 2 , x , Debug 1 ]
Calculating limit of x - x + H H - x - x 1 + H H 2
Calculating Mrv set of x - x + H H - x - x 1 + H H 2
Ω= { MrvF [ x , 3 , ] , MrvF [ - x , 5 , 0 ] , MrvF [ H , 14 , 0 ] , MrvF [ H - x , 20 , 0 ] , MrvF [ - x 1 + H , 23 , 0 ] }
MrvSet hat 5 verschiedene Teilausdrücke gefunden, die allesamt exponentielles Wachstum besitzen, und hat diese auch gleich nach Komplexität geordnet: e x , e - x , h, e h -x und e - x 1 +h . e x strebt dabei gegen ∞, die anderen streben gegen 0.

Es sei noch mal darauf hingewiesen, dass der Term H nur in der Ausgabe auftaucht, der Algorithmus selbst arbeitet dagegen immer mit dem kompletten Funktionsterm.
ω= x
Replacing ω→1/ω: ω= - x
Als ω wurde der einfachste Term e x gewählt. Da aber immer ein Term mit Grenzwert 0 gewählt werden muss, wird stattdessen e - x verwendet, der diesmal zufälligerweise auch in Ω liegt.
Prepare to rewrite g= - x 1 + H to A*ω^c
c= 1 A= x - x 1 + H
Rewriting - x 1 + H x - x 1 + H ω 1
f= x - x + H H - x x - x 1 + H ω 1 H 2
Der Algorithmus hat sich den komplexesten Term e - x 1 +h ausgesucht und schreibt ihn in einen Term A ω c um. Der statt dessen eingesetzte Term e x -x 1 +h hat eine niedrigere Wachstumsklasse, erst das außerdem auftauchende ω 1 erzeugt wieder das alte Wachstum.

Man sieht auch, dass durch die Ersetzung ein Term h hinzugekommen ist, der dann später ersetzt wird. Deswegen ist die Reihenfolge der Ersetzung so entscheidend.

Die weiteren Ersetzungen erfolgen analog:
Prepare to rewrite g= H - x to A*ω^c
c= 1 A= H
Rewriting H - x H ω 1
f= x - x + H x - x 1 + H H ω 1 ω 1 H 2
Prepare to rewrite g= H to A*ω^c
c= 1 A= x - x 1 + - x
Rewriting H x - x 1 + - x ω 1
f= x - x + x - x 1 + - x ω 1 ω 1 x - x 1 + - x ω 1 x - x 1 + x - x 1 + - x ω 1 ω 1 ( x - x 1 + - x ω 1 ) 2
Prepare to rewrite g= - x to A*ω^c
c= 1 A= 1
Rewriting - x ω 1
f= x - x + x - x 1 + ω 1 ω 1 ω 1 x - x 1 + ω 1 ω 1 x - x 1 + x - x 1 + ω 1 ω 1 ω 1 ( x - x 1 + ω 1 ω 1 ) 2
Prepare to rewrite g= x to A*ω^c
c= - 1 A= 1
Rewriting x 1 ω
f= x - 1 ω + x - x 1 + ω 1 ω 1 ω 1 x - x 1 + ω 1 ω 1 x - x 1 + x - x 1 + ω 1 ω 1 ω 1 ( x - x 1 + ω 1 ω 1 ) 2
Damit ist die Funktion vollständig transformiert. Das exponentielle Wachstum konzentriert sich in ω, alle anderen, nicht von ω abhängigen Teilausdrücke haben niedrigere Ordnung. Der Weg ist frei für die Potenzreihenentwicklung:
Calculating power series: f= x - 1 ω + x - x 1 + ω 1 ω 1 ω 1 x - x 1 + ω 1 ω 1 x - x 1 + x - x 1 + ω 1 ω 1 ω 1 ( x - x 1 + ω 1 ω 1 ) 2
Series expansion: 2 + ( 3 + 3 x 2 2 ) ω + O [ ω ] 2
Dominant term: 2 ω 0 ,ω= - x
Damit zerfällt die scheinbar so komplexe Funktion zu nichts. Der führende Term hat die Ordnung 0, das Wachstum von e - x löscht sich also vollständig aus. Der Restterm niedrigerer Wachstumsklasse ist konstant 2, deswegen sind keine weiteren Berechnungen erforderlich, das Endergebnis ist 2:
Dominant asymptotic: 2 ( - x ) 0
Leave Level 1 Call MrvLimitInf [ x - x + - x + H H - x 1 + H H 2 , x , Debug 1 ] == 2
Out[6]=
2

6.2 Überlagerung unterschiedlichen Wachstums

Als nächstes folgt ein typisches Beispiel für das Auftreten verschiedener Wachstumsklassen in einem Term und deren stückweise Abarbeitung. Das Beispiel stammt von
[Gru96], Beispiel 3.5 von Seite 60, geht aber auf [RSSH96] zurück. Die Debugausgabe ist hier nur in gekürzter Form wiedergegeben.
In[1]:=
f = Log [ Log [ x E ^ ( x E ^ x ) + 1 ] ] - E ^ E ^ ( Log [ Log [ x ] ] + 1 / x )
Out[1]=
- x 1 x + Log [ Log [ 1 + x x x ] ]
In[2]:=
MrvLimit [ f , x , Debug 1 ]
Enter Level 1 Call MrvLimitInf [ Log [ Log [ 1 + x x x ] ] - Log [ x ] 1 x , x , Debug 1 ]
Ω= { MrvF [ x x , 7 , ] }
Der Term mit höchster Wachstumsklasse ist gefunden, und er tritt sogar nur einmal in der Funktion auf. Wir überspringen hier das Umschreiben der Funktion und verfolgen als nächstes die Potenzreihenentwicklung:
Calculating power series: f= Log [ Log [ 1 + x ω ] ] - Log [ x ] 1 x
Dominant term: ( Log [ Log [ x ] + x x ] - Log [ x ] 1 x ) ω 0 ,ω= - x x
Trotz dass der ω-Term genau einmal in der Funktion auftritt, sein Beitrag zum Grenzwert löscht sich aus und ist von der Ordnung 0. Der Koeffizient hängt aber weiterhin von x ab, es kommt also zu einer weiteren Grenzwertberechnung.
Calculating limit of Log [ Log [ x ] + x x ] - Log [ x ] 1 x
Ω= { MrvF [ x , 3 , ] }
Das nächst schwächere Wachstumsverhalten ist e x und taucht auch nur einmal in der Funktion auf. Wir springen wieder direkt zur Potenzreihe:
Calculating power series: f= Log [ Log [ x ] + x ω ] - Log [ x ] 1 x
Dominant term: ( x + Log [ x ] - Log [ x ] 1 x ) ω 0 ,ω= - x
Auch diese Wachstumsklasse ist von der Ordnung 0, eine dritte Grenzwertberechnung wird nötig.
Calculating limit of x + Log [ x ] - Log [ x ] 1 x
Ω= { MrvF [ x , 1 , ] , MrvF [ Log [ x ] 1 x , 10 , ] }
Scale Up: f= x + x - x 1 x
Ω= { MrvF [ x , 3 , ] , MrvF [ x 1 x , 11 , ] }
ω= x
Replacing ω→1/ω: ω= - x
Diesmal bleibt als Wachstumsklasse nur x übrig, sowie ein weiterer Term mit polynomiellem Wachstumsverhalten: e log (x)e 1 /x . Obwohl auf den ersten Blick eine Exponentialfunktion, wächst der Term doch insgesamt asymptotisch nur polynomiell.

Da der Algorithmus auf exponentiellem Wachstum aufbaut, ist jetzt erstmals eine Anhebung der Wachstumsklasse erforderlich gewesen. Die Ersetzung war erfolgreich, beide Terme treten weiterhin in der Funktion auf und sind nun exponentiell.
Calculating power series: f= x + 1 ω - - x + x - x ω
Series expansion: ( - x 2 - x 2 2 ) ω + O [ ω ] 2
Scale Down: f= - Log [ x ] 2 - Log [ x ] 2 2 , ω= - Log [ x ]
Dominant term: ω 1 ( - Log [ x ] 2 - Log [ x ] 2 2 ) , ω= - Log [ x ]
Diesmal tritt die Wachstumsklasse real auf und wird nicht durch Auslöschungseffekte eliminiert. Nachdem die Anhebung der Wachstumsklasse rückgängig gemacht wurde, bleibt der etwas seltsame Term ω =e - logx übrig, was natürlich nichts anderes als 1 /x ist. Die Funktion hat also das Wachstumsverhalten von x - 1 .

Eigentlich ist damit bereits alles klar, die Funktion muss den Grenzwert 0 haben, auch der noch ungeklärte Koeffizient - log x2 -log (x) 22 kann daran nichts mehr ändern. Der Algorithmus analysiert die Funktion aber trotzdem vollständig bis zum Ende weiter.
Calculating limit of - Log [ x ] 2 - Log [ x ] 2 2
Ω= { MrvF [ x , 1 , ] }
Scale Up: f= - x 2 - x 2 2
Ω= { MrvF [ x , 1 , ] }
Scale Up: f= - x 2 - ( x ) 2 2
Ω= { MrvF [ x , 3 , ] }
ω= x
Replacing ω→1/ω: ω= - x
Damit war zu rechnen: Die Wachstumsklasse x war bereits eliminiert, also sind diesmal mindestens zwei Anhebungen der Wachstumsklasse erforderlich. Die erste Anhebung führt dabei gleich wieder zu dem Term, der in der Vorrunde aus der Potenzreihenentwicklung hervorging.
Calculating power series: f= - 1 2 ω - 1 2 ( 1 ω ) 2
Series expansion: - 1 2 ω 2 - 1 2 ω + O [ ω ] 2
Scale Down: f= - 1 2 , ω= - Log [ x ]
Scale Down: f= - 1 2 , ω= - Log [ Log [ x ] ]
Dominant term: - 1 2 ω 2 , ω= - Log [ Log [ x ] ]
Damit sind alle Wachstumsklassen abgearbeitet. Übrig bleibt - 1 2 (e - loglogx) - 2 , oder einfacher - 1 2 log(x) 2 . Es folgt nur noch die Schlussbetrachtung:
Dominant asymptotic: - 1 2 ( - x x ) 0 ( - x ) 0 ( - Log [ x ] ) 1 1 ( - Log [ Log [ x ] ] ) 2
Leave Level 1 Call MrvLimitInf [ Log [ Log [ 1 + x x x ] ] - Log [ x ] 1 x , x , Debug 1 ] == 0
Out[2]=
0
Die ersten beiden Funktionen, e - xe x und e - x , haben starkes Wachstum, gehen in die asymptotische Grenzwertentwicklung jedoch nicht ein, deswegen lautet die Asymptote nur - 1 2 x - 1log(x) 2 . Der Grenzwert ist, wie schon vorher klar war, 0. Als zusätzliche Erkenntnis bleibt noch zu erwähnen, dass der Grenzwert sich von unten an die 0 annähert, da das Vorzeichen des konstanten Faktors negativ ist.

6.3 Große Erfolge

In diesem Kapitel untersuchen wir die Kompatibilität und Performance des Algorithmus in unterschiedlichen Mathematica-Versionen. Als Vergleich darf sich auch Mathematicas eingebaute Limit-Funktion abmühen.
[Gru96] gibt dazu in Kapitel 8 eine stattliche Liste von schwierig zu lösenden Grenzwertaufgaben vor:
8 .1 lim x e x ( e 1 /x-e - x -e 1 /x )=-1 8 .2 lim x e x ( e e - x +e - x 2 +1/x -e 1 /x-e - e x )=1 8 .3 lim x e e x -e - x 1 -1/x -e e x = 8 .4 lim x exp ( exp( e x 1 -1/x ) )-exp ( exp( e x - ln (x) - ln(x) -1/x+1 ) )=- 8 .5 lim x exp ( exp( e x +e - x ) )exp ( exp( e x ) ) = 8 .6 lim x exp ( exp( e x ) ) exp ( exp( exp( x-e - e x ) ) ) = 8 .7 lim x exp ( exp( e x ) ) exp ( exp( exp( x-e - e e x ) ) ) =1 8 .8 lim x e e x exp ( exp( x-e - e e x ) ) =1 8 .9 lim x ln (x) 2 e ln (x) ln (ln(x)) 2 e ln (ln(x)) ln (ln(ln(x))) 3 x =0 8 .10 lim x x ln(x)ln ( xe x -x 2 ) 2 ln ( ln( x 2 +2e e 3 x 3 ln(x) ) )=1 3 8 .11 lim x ( e ( xe - x ) /( e - x +e - 2 x 2 x +1 ) -e x )/x=-e 2 8 .12 lim x ( 3 x +5 x ) 1 /x =5 8 .13 lim x x/ln( x ln ( x ln (2)/ln(x) ) )= 8 .14 lim x exp ( exp( 2ln( x 5 +x )ln(ln(x)) ) ) exp ( exp( 10ln(x)ln(ln(x)) ) ) = 8 .15 lim x 4 exp ( exp( 5 2 x - 5/7 +21 8 x 6 /11 +54 17 x 49 /45 +2x - 8 ) ) 8 9 ln ( ln( -ln( 4 3 x - 5/14 ) ) ) ( 7/6) = 8 .16 lim x exp ( 4 xe - x ( e x) - 1+( exp ( (2x 2 )/(x+1) ) ) - 1 ) -e x ( e x ) 4 =1 8 .17 lim x exp ( x e - x e - x +exp ( -2 x +1x 2 ) ) e x =1 8 .18 lim x x-e x +e e - x 1 +e - x e - x 1 +e - x 1 +e - x e e e - x 1 +e - x -x ( e - x 1 +e - x ) 2 =2 8 .19 lim x ( ln(ln(x)+ln(ln(x)))-ln(ln(x)))ln(x)ln (ln(x)+ln(ln(ln(x))))=1 8 .20 lim x e ln ( ln( x+e ln (x)ln(ln(x)) ) )ln (ln(ln(x+e x +ln(x)))) =e 8 .21 lim x e x ( sin( e - x +1/x )-sin( e - x 2 +1/x ) )=1 8 .22 lim x e e x ( e sin ( e - e x +1/x ) -e sin ( 1/x ) )=1 8 .26 lim x e x (Γ(x+e - x )-Γ(x))= 8 .27 lim x e Γ (x-e - x )e 1 /x -e Γ (x) = 8 .28 lim x Γ ( x+1 Γ (x) )-Γ(x)ln (x)=1 8 .29 lim x x( -Γ(x)+Γ( x-1 Γ (x) )+ln(x) )=1 2 8 .30 lim x ( Γ ( x+1 Γ (x) )-Γ(x)ln (x)-cos( 1/x ) )xln(x)=-1 2 8 .31 lim x Γ (x+1)2 π -e - x ( x x +1 2 +1 12 x x -1 2 )= 8 .32 lim x ln (Γ(Γ(x)))e x = 8 .33 lim x e e ψ ( ψ (x) ) x=1 e 8 .34 lim x e e ψ (ln(x)) x=1 e 8 .35 lim x e e e ψ ( ψ ( ψ (x) ) ) x=0
Jeweils unter Mathematica 3.0, Mathematica 4.2 und Mathematica 5.0 wurde jede dieser Grenzwertaufgaben einmal mit Mathematicas eigener Limit-Funktion und einmal mit MrvLimit berechnet. Um die Rechenzeit in Grenzen zu halten, wurde die Berechnung nach 60 Sekunden abgebrochen. Um Chancengleichheit zu gewähren, wurden außerdem vor jeder Berechnung eventuell gespeicherte Ergebnisse vorheriger Durchläufe gelöscht. Der Aufruf hat damit für Mathematicas Limit-Funktion folgende Form:
	Developer`ClearCache[];
	Timing[TimeConstrained[Limit[f, x→∞], 60, Timeout]];
Der Aufruf von MrvLimit hatte die folgende Form:
	Developer`ClearCache[];
	MrvLimitClearCache[];
	Timing[TimeConstrained[MrvLimit[f, x→∞], 60, Timeout]];
Hier die Ergebnisse der Testläufe:



{
Aufgabe MrvLimit Limit
Math. 5.0 Math. 4.2 Math. 3.0 Math. 5.0 Math. 4.2 Math. 3.0
8.1 Ok0.1s Ok0.1s Ok0.3s Ok0.1s Ok0.8s --1.9s
8.2 Ok1.1s Ok0.5s Ok1.0s --5.8s Ok41.1s --0.0s
8.3 Ok1.2s Ok1.0s Ok1.6s Ok0.8s Ok0.7s --0.0s
8.4 Ok3.6s Ok2.5s Ok5.3s Ok7.3s Ok0.6s --0.3s
8.5 Ok1.4s --1.0s --1.8s Ok0.1s Ok0.0s --0.0s
8.6 Ok0.8s Ok0.7s Ok1.2s --0.8s --0.1s --0.1s
8.7 Ok0.9s Ok0.9s Ok1.6s --0.7s --0.4s --0.1s
8.8 Ok0.6s Ok0.6s Ok1.0s --0.5s Ok0.1s --0.0s
8.9 Ok0.4s Ok0.4s Ok0.8s F0.8s Ok4.4s --0.1s
8.10 Ok0.4s Ok0.3s Ok0.7s --17.7s --50.3s --3.4s
8.11 Ok0.6s Ok0.5s Ok0.8s --5.0s --1.8s --2.3s
8.12 Ok0.0s --0.1s --0.0s Ok0.1s --0.8s --1.9s
8.13 Ok0.0s Ok0.0s Ok0.0s Ok0.0s Ok0.0s Ok0.0s
8.14 Ok0.6s Ok0.4s Ok0.9s --1.1s --0.4s --0.0s
8.15 Time out Time out Time out F42.3s F6.7s --0.4s
8.16 Ok0.8s Ok0.9s Ok1.0s Ok1.3s Ok1.6s --2.3s
8.17 Ok0.3s Ok0.2s Ok0.4s Ok0.3s Ok0.3s --2.2s
8.18 Ok7.1s Ok2.3s Ok2.1s --25.0s --0.5s --0.3s
8.19 Ok0.0s Ok0.0s Ok0.1s Ok0.5s --1.1s --0.1s
8.20 --0.0s --0.3s --0.6s --8.8s --6.4s --11.9s
8.21 Ok0.3s Ok0.3s Ok0.6s --6.2s Ok2.0s --1.9s
8.22 Ok0.4s Ok0.3s Ok0.7s F1.0s --0.6s --2.4s
8.26 Ok0.9s --0.1s --0.3s Time out --0.1s --0.1s
8.27 Ok2.5s --0.1s --0.4s Time out --0.0s --0.1s
8.28 Ok0.5s --0.1s --0.3s --47.5s --0.0s --0.1s
8.29 Ok0.6s --0.1s --0.4s --44.4s --0.1s --0.2s


{
Aufgabe MrvLimit Limit
Math. 5.0 Math. 4.2 Math. 3.0 Math. 5.0 Math. 4.2 Math. 3.0
8.30 Ok0.7s --0.1s --0.4s Time out --0.1s --0.4s
8.31 Ok0.4s --0.2s --0.4s --1.6s --1.2s --0.0s
8.32 Ok0.2s --0.1s --0.3s --12.5s --0.0s --0.1s
8.33 Ok1.3s --0.0s --0.1s F1.4s --0.0s --0.0s
8.34 Ok0.3s --0.0s --0.1s F2.8s F0.0s --0.3s
8.35 Ok10.4s --0.1s --0.3s F27.7s --0.0s --0.0s


Ok:Grenzwert wurde korrekt berechnet
--:Kein Ergebnis gefunden
F:Falsches Ergebnis gefunden
Time out:Maximal zulässige Zeit (60s) überschritten


Am katastrophalsten schneidet Mathematica 3.0 ab. Dass wenigstens Aufgabe 8.13 erfolgreich gelöst wird, liegt an der Tatsache, dass die Funktion bei Eingabe sofort zu x /ln(x ln 2) vereinfacht wird.

Mathematica 4.2 kommt mit einigen Aufgaben schon erheblich besser zurecht, scheitert aber noch an zu vielen Aufgaben, insbesondere an allen Aufgaben, die die Gamma-Funktion betreffen. In diesem Bereich gibt sich dann Mathematica 5.0 sehr viel Mühe, leider auch mit wenig Erfolg. Im Bereich der normalen Funktionen ist dagegen nur eine Verschiebung, aber kaum eine Verbesserung zu bemerken.

Schwer wiegt auch, dass sich Mathematica mit seiner Limit-Funktion falsche Ergebnisse erlaubt. Eine Aufgabe als nicht lösbar abzuweisen ist wenigstens ehrlich, ein falsches Ergebnis wird dagegen nur allzu oft als wahr hingenommen.

MrvLimit schneidet dagegen erwartungsgemäß gut ab. Unter Mathematica 5.0 trüben nur zwei Fehlschläge das Bild. Eine genauere Fehleranalyse findet im folgenden Kapitel statt. Unter Mathematica 4.2 und 3.0 ergibt sich ein identisches Bild: Zwei weitere Grenzwerte können nicht ermittelt werden, außerdem fallen sämtliche Aufgaben, die die Gamma-Funktion betreffen, aus. Schuld daran ist, dass das Series-Kommando erst seit Version 5.0 brauchbar mit der Gamma-Funktion umgehen kann.

Bleibt noch ein abschließender Blick auf die Performance: Die Rechenzeit von MrvLimit schneidet fast immer gut ab, lange Denkzeiten sind selten. Fast immer ist MrvLimit auch schneller als Limit.

Ungewöhnlich ist auf den ersten Blick jedoch, dass MrvLimit unter Mathematica 4.2 schneller zu sein scheint, als unter Mathematica 5.0. Der Grund dafür ist der geringere Arbeitseifer des Simplify-Kommandos, das für die Nulltests des Algorithmus verantwortlich ist. Die Performance des gesamten Algorithmus hängt tatsächlich hauptsächlich von der Performance des Series-Kommandos und des Simplify-Kommandos ab.

6.4 Fehlschläge

Nicht alles lief einwandfrei, deswegen werfen wir einen detaillierten Blick auf die 4 Fälle, in denen MrvLimit versagt hat.

6.4.1 Aufgabe 8.15

In Fall von Aufgabe 8.15 legt MrvLimit auf allen Plattformen eine Denkpause epischer Länge ein und kommt auch nach dreistündiger Rechenzeit noch zu keinem Ergebnis. Verfolgt man den Ablauf, so kommt es in der dritten Rekursionsstufe zu folgendem Aufruf:
Calculating power series: f= 2 ( 1 ω ) 8 + 5 2 ( 1 ω ) 5 / 7 + 21 8 ( 1 ω ) 6 / 11 + 54 17 ( 1 ω ) 49 / 45 - 2 ( 1 ω ) 8 - 5 2 ( 1 ω ) 5 / 7 - 21 8 ( 1 ω ) 6 / 11 - 54 17 ( 1 ω ) 49 / 45
Offensichtlich unterscheiden sich Zähler und Nenner nur durch das entgegengesetzte Vorzeichen. Mathematica bemerkt das aber nicht und wird davon derart verwirrt, dass die Rechenzeit explodiert. Hilft man Mathematica über diese Hürde hinweg, ist die Aufgabe korrekt lösbar:
In[1]:=
Unprotect [ Series ] ;
In[2]:=
Series [ 2 ( 1 ω_ ) 8 + 5 2 ( 1 ω_ ) 5 / 7 + 21 8 ( 1 ω_ ) 6 / 11 + 54 17 ( 1 ω_ ) 49 / 45 - 2 ( 1 ω_ ) 8 - 5 2 ( 1 ω_ ) 5 / 7 - 21 8 ( 1 ω_ ) 6 / 11 - 54 17 ( 1 ω_ ) 49 / 45 , { ω_ , 0 , _ } ] = - 1 ;
In[3]:=
Protect [ Series ] ;
In[4]:=
MrvLimit [ StandardExamples [ [ 15 , 2 ] ] , x ]
Out[4]=

6.4.2 Aufgabe 8.20

Bei Aufgabe 8.20 scheitert MrvLimit auch auf allen Plattformen, diesmal mit einer konkreten Fehlermeldung:
In[1]:=
MrvLimit [ StandardExamples [ [ 20 , 2 ] ] , x , Debug 1 ]
(Gekürzt...)
Calculating power series: f= Log [ x ω ] x
Series expansion: ( x ω ) 1 x
MrvLimit :: SeriesFail : Series failed at Log [ x ω ] x
Schauen wir doch mal, was Mathematica aus dieser Funktion macht, bevor sie an Series weitergegeben wird:
In[2]:=
Log [ x ω ] x
Out[2]=
( x ω ) 1 x
Da hat die automatische Vereinfachung von Mathematica mal wieder ganze Arbeit geleistet. Wir können Mathematica aber zwingen, die Potenzreihenentwicklung zuerst auf den Exponenten anzuwenden, um so die Vereinfachung zu unterdrücken.
In[3]:=
Unprotect [ Series ] ;
In[4]:=
Series [ Log [ x ω_ ] x , { ω_ , 0 , k_ } ] := E ^ Series [ Log [ x ω ] x , { ω , 0 , k } ] ;
In[5]:=
Protect [ Series ] ;
In[6]:=
MrvLimit [ StandardExamples [ [ 20 , 2 ] ] , x ]
Out[6]=
Na, warum denn nicht gleich so? Dieses Ergebnis ist jedenfalls korrekt.

6.4.3 Aufgabe 8.5

Aufgabe 8.5 wird von Mathematica 5.0 fehlerfrei gelöst, von Mathematica 4.2 und 3.0 jedoch nicht. Woran liegt es?
In[1]:=
MrvLimit [ StandardExamples [ [ 5 , 2 ] ] , x , Debug 1 ]
Calculating power series: f= - x - ( - 1 + ) ( x - x + - x ) 1 - + x + - x ω - 1 + 1 -
Series expansion: Series [ - x + - x + x - ( - 1 + ) ( x - - x + x ) 1 - ω - 1 + 1 - , { ω , 0 , 1 } ]
MrvLimit :: SeriesFail : Series failed at - x - ( - 1 + ) ( 1 - 1 ) 1 - + x + - x ω - 1 + 1 -
Offensichtlich bereitet der Term ω - 1+e1 -e Mathematica 4.2 erheblich Bauchschmerzen. Auf den ersten Blick scheint das ein irrationaler Exponent zu sein, und irrationale Exponenten verkraftet das Series-Kommando nicht. Würde Mathematica etwas genauer hinschauen, wäre vielleicht aufgefallen, dass der Exponent schlicht -1 ist...

Doch warum scheitert Mathematica 5.0 nicht an dieser Stelle? Die gleiche Situation tritt jedenfalls auch auf:
Calculating power series: f= - x - ( - 1 + ) ( x - x + - x ) 1 - + x + - x ω - 1 + 1 -
Series expansion: - x + - x + x - ( - 1 + ) ( x - - x + x ) 1 - ω - 1 + 1 -
Auch Mathematica 5.0 erkennt den Exponenten nicht als -1, erkennt aber sehr wohl, dass es ein polynomieller Term mit Exponent 1 ist und reicht ihn direkt unmodifiziert durch Series hindurch, auf das der Aufrufer sehe, was er damit anfange...

Es bleibt danach für das Series-Kommando nur noch die Nullfunktion übrig, wodurch die eigentliche Potenzreihe gleich vollständig verschwindet. Daher taucht auch kein O[ω] auf.

6.4.4 Aufgabe 8.12

Auch mit Aufgabe 8.12 hat Mathematica 5.0 mehr Erfolg, als seine Vorgänger. Werfen wir wieder einen Blick auf das Problem:
In[1]:=
MrvLimit [ StandardExamples [ [ 12 , 2 ] ] , x , Debug 1 ]
Calculating power series: f= Log [ 1 ω + x Log [ 3 ] - x Log [ 5 ] Log [ 3 ] Log [ 5 ] ω - Log [ 3 ] Log [ 5 ] ] x
Series expansion: Series [ ( 3 x + 1 ω ) 1 x , { ω , 0 , 1 } ]
MrvLimit :: SeriesFail : Series failed at Log [ 1 ω + 1 ω - 1 ] x
Hier lohnt wieder ein Blick auf die Funktion, wie Mathematica sie vereinfacht, bevor das Series-Kommando seine Arbeit beginnt:
In[2]:=
Log [ 1 ω + x Log [ 3 ] - x Log [ 5 ] Log [ 3 ] Log [ 5 ] ω - Log [ 3 ] Log [ 5 ] ] x
Out[2]=
( 1 ω + ω - Log [ 3 ] Log [ 5 ] ) 1 x
Die Ähnlichkeit zum Problem von 8.20 ist kaum zu übersehen. Vermutlich würde der damalige Trick auch hier funktionieren. Diesmal kommt Mathematica 5.0 aber an gleicher Stelle besser mit der Funktion zurecht:
Calculating power series: f= Log [ 1 ω + x Log [ 3 ] - x Log [ 5 ] Log [ 3 ] Log [ 5 ] ω - Log [ 3 ] Log [ 5 ] ] x
Series expansion: 5 + 5 3 x ω x + O [ ω ] 2

6.4.5 Schlussfolgerung

Alle vier Beispiele zeigen deutlich, dem Algorithmus ist kein Vorwurf zu machen. Der MrvLimit-Algorithmus scheitert nur an den Nahtstellen zu Mathematica. Natürlich sind die Beispiele von vornherein so ausgesucht, dass sie vom Algorithmus auch bewältigt werden können. Doch der Vergleich zur Limit-Funktion zeigt deutlich die Überlegenheit, wenn es um Funktionen dieser Kategorie geht.

Zwei Fehlerquellen treten derzeit noch deutlich hervor: Zum einen ist Mathematicas Angewohnheit, jeden Term ungefragt umzuschreiben, hier ein ernstzunehmendes Problem. Kann das Umschreiben Algorithmus-intern noch umgangen werden, so ist spätestens bei der Übergabe an Series die mühsam entwickelte Funktionsstruktur oft wieder hinfällig. Mathematica wäre gut beraten, zumindest die Möglichkeit vorzusehen, bestimmte Vereinfachungen vorübergehend zu deaktivieren.

Die zweite Fehlerquelle ist das Series-Kommando. Seine Beschränkung auf Puiseux-Reihen, deren Exponenten rational mit gemeinsamen Hauptnenner sein müssen, erfüllt im Extremfall schon nicht die Anforderungen des MrvLimit-Algorithmus. Eine Implementierung eines Series-Kommandos für generalisierte Potenzreihen wäre sicher nicht nachteilig, insbesondere wenn Funktionen ebenfalls in Algorithmus-interner Darstellung übergeben werden könnten, anstatt sie zuerst in allgemeine Mathematica-Terme zurück zu übersetzen.

6.5 Grenzen

In diesem Kapitel geht es um die Grenzen des Algorithmus und wie man sie mit ein paar Handgriffen vielleicht umgehen kann, um doch noch zu einer Lösung zu kommen. Eine solche Grenze ist die Verwendung von symbolischen Konstanten.

Schon eine einfache Aufgabe wie lim x x c hat keine allgemeine Lösung, sondern hängt direkt vom Vorzeichen von c ab. Genauso problematisch ist lim x (-1) cx , bei dem die Lösung sogar vom konkreten Wert von c abhängt. Kann man solche Aufgaben überhaupt mit Computeralgebra lösen?

Im Laufe der Tests trat folgende Aufgabe auf:
In[4]:=
Δ = b 2 - 4 a c ;
In[5]:=
f = Δ ^ n * Pochhammer [ d / ( 2 * a ) - ( 2 * a * e - b * d ) / ( 2 * a * Δ ) , n ]
In[6]:=
/ ( ( - a ) ^ n * Pochhammer [ n - 1 + d / a , n ] )
Out[6]=
( - a ) - n ( b 2 - 4 a c ) n / 2 Pochhammer [ d 2 a - - b d + 2 a e 2 a b 2 - 4 a c , n ] Pochhammer [ - 1 + d a + n , n ]
In[7]:=
f = f /. Pochhammer [ x_ , n_ ] Gamma [ x + n ] / Gamma [ x ]
Out[7]=
( - a ) - n ( b 2 - 4 a c ) n / 2 Gamma [ - 1 + d a + n ] Gamma [ d 2 a - - b d + 2 a e 2 a b 2 - 4 a c + n ] Gamma [ d 2 a - - b d + 2 a e 2 a b 2 - 4 a c ] Gamma [ - 1 + d a + 2 n ]
Gesucht ist der Grenzwert für a 0 . Wir beschränken uns hier auf a >0 , der Grenzwert bei Annäherung von unten verläuft ähnlich. Außerdem beschränken wir uns auf den interessanten Fall d >0 , damit die Gamma-Funktion Γ (-1+d/a+n) gegen + läuft.

Um Mathematica mitzuteilen, dass d >0 sein soll, genügt es, diese Bedingung direkt in das Sign-Kommando zu integrieren:
In[4]:=
Unprotect [ Sign ] ; Sign [ d ] = 1 ; Protect [ Sign ] ;
In[5]:=
MrvLimit [ f , a 0 , Direction - 1 ]
MrvLimit :: UnsupportedArgument : MrvLimit does not support Gamma [ a ] for a Sign [ d 2 + b 2 d 2 b ] , appearing in Gamma [ a d 2 + n - a ( - b d + 2 e a ) 2 b 2 - 4 c a ]
Nun, das funktioniert noch nicht so, wie geplant. Offensichtlich ist das Vorzeichen von d 2 +b 2 d2 b von entscheidender Rolle. Da aber
Sign [ d 2 +b 2 d2 b ]=Sign[ d 2 ]Sign[ 1+b 2 b ]
gilt, ist das kritische Vorzeichen das von b. Ist b >0 , so ist der letzte Term 2, für b <0 dagegen 0.

Beschäftigen wir uns zunächst mit b >0 . Wieder weisen wir das Vorzeichen von b direkt Sign zu. Außerdem werden wir den Term b 2 direkt zu b vereinfachen, um auch diese Falle gleich zu entschärfen:
In[6]:=
Unprotect [ Sign ] ; Sign [ d ] = 1 ; Sign [ b ] = 1 ; Protect [ Sign ] ;
In[7]:=
Unprotect [ Power ] ; b 2 = b ; Protect [ Power ] ;
In[8]:=
MrvLimit [ f , a 0 , Direction - 1 , Debug 1 ]
Ω= { MrvF [ a , 1 , ] }
ω= a
Replacing ω→1/ω: ω= - a
Series expansion: ( - ω ) - n ( ( b 2 ) n / 2 + ( - 2 ( b 2 ) - 1 + n 2 c n + ( b 2 ) n / 2 ( c n b 2 + n d - e n b d - n 2 d ) ) ω + O [ ω ] 2 )
MrvLimit :: SeriesFail : Series failed at LogGamma [ - 1 + n + d ω ] - 1 - 1 + LogGamma [ n + d 1 - - b d + 1 1 2 ω 1 - 1 ] ( - 1 1 ω ) - n ( b 2 - 4 c 1 ω ) n / 2
Die Ausgabe von Series enthält hier einen Faktor ( -ω) - n . Das genaue Verhalten dieses Faktors ist natürlich abhängig vom Vorzeichen von n, deswegen scheitert der Algorithmus hier. Die einzige Alternative ist, den Faktor von Hand aus der Aufgabe zu entfernen. ω ist in diesem Fall e a , allerdings wurde der Term einmal in eine höhere Wachstumsklasse angehoben, und auch die Richtung a 0 + spielt eine Rolle. Der richtige Faktor, um ( -ω) - n zu neutralisieren, lautet insgesamt ( -a) n .
In[9]:=
MrvLimit [ f * ( - a ) ^ n , a 0 , Direction - 1 ]
Out[9]=
( b 2 ) n / 2
Damit ist im Fall b >0 der Grenzwert:
lim a 0 + f=b nlim a 0 +(-a) - n=(-1) nb nlim a 0 +a - n


Betrachten wir nun den Fall b <0 . Diesmal ändert sich eine wesentliche Randbedingung, das Vorzeichen von b. Deswegen muss hier auch unbedingt der Cache der berechneten Grenzwerte geleert werden, da einige der gespeicherten Ergebnisse nur unter der Annahme b >0 gültig sind.
In[10]:=
Unprotect [ Sign ] ; Sign [ d ] = 1 ; Sign [ b ] = - 1 ; Protect [ Sign ] ;
In[11]:=
Unprotect [ Power ] ; b 2 = - b ; Protect [ Power ] ;
In[12]:=
MrvLimitClearCache [ ] ;
In[13]:=
MrvLimit [ f , a 0 , Direction - 1 ]
MrvLimit :: UnknownSign : Cannot determine sign: n ( - )


Anscheinend müssen wir in diesem Fall auch das Vorzeichen von n beachten. Wir überprüfen zunächst b <0,n>0 :
In[14]:=
Unprotect [ Sign ] ; Sign [ d ] = 1 ; Sign [ b ] = - 1 ; Sign [ n ] = 1 ; Protect [ Sign ] ;
In[15]:=
Unprotect [ Power ] ; b 2 = - b ; Protect [ Power ] ;
In[16]:=
MrvLimitClearCache [ ] ;
In[17]:=
MrvLimit [ f , a 0 , Direction - 1 , Debug 1 ]
Series expansion: ( - ω ) - n ω n ( ( b 2 ) n / 2 d - n Gamma [ - c d b 2 + e b + n ] Gamma [ - c d + b e b 2 ] + ( Gamma [ - c d b 2 + e b + n ] ( - 2 ( b 2 ) - 1 + n 2 c d - n n + ( b 2 ) n / 2 ( 3 2 d - 1 - n n - 3 2 d - 1 - n n 2 ) Gamma [ - c d + b e b 2 ] - ( b 2 ) n / 2 d - n ( - 3 c 2 d b 4 + 2 c e b 3 ) PolyGamma [ 0 , - c d + b e b 2 ] Gamma [ - c d + b e b 2 ] ) + ( b 2 ) n / 2 d - n ( - 3 c 2 d b 4 + 2 c e b 3 ) Gamma [ - c d b 2 + e b + n ] PolyGamma [ 0 , - c d b 2 + e b + n ] Gamma [ - c d + b e b 2 ] ) ω + O [ ω ] 2 )
MrvLimit :: SeriesFail : Series failed at Gamma [ n + d 2 ω - - b d + 2 e 1 ω 2 ω b 2 - 4 1 1 1 ] 1 1 1 ( 1 - 1 ) n / 2 Gamma [ d 2 ω - - b d + 2 e 1 ω 2 ω b 2 - 4 1 1 1 ]
Der Term ( -ω) - nω n bereitet diesmal die Bauchschmerzen. Der Faktor wird kompensiert durch den Faktor ( -a) na - n :
In[18]:=
MrvLimit [ f * ( - a ) ^ n * a ^ ( - n ) , a 0 , Direction - 1 ]
Out[18]=
( b 2 ) n / 2 d - n Gamma [ - c d b 2 + e b + n ] Gamma [ - c d + b e b 2 ]
Da ( -a) - na n=(-1) n und ( b 2) n /2=( b 2 ) n=(-b) n ist, ergibt sich im Fall b <0,n>0 der Grenzwert:
lim a 0 + f=(-1) n(-b) nd - nΓ (-cdb - 2+eb - 1+n)Γ (-cdb - 2+eb - 1))=b nd - nPochhammer( -cdb - 2+eb - 1,n )


Nächster Schritt ist b <0,n<0 . Wir versuchen gleich die Berechnung mit dem gleichen Vorfaktor wie eben:
In[19]:=
Unprotect [ Sign ] ; Sign [ d ] = 1 ; Sign [ b ] = - 1 ; Sign [ n ] = - 1 ; Protect [ Sign ] ;
In[20]:=
Unprotect [ Power ] ; b 2 = - b ; Protect [ Power ] ;
In[21]:=
MrvLimitClearCache [ ] ;
In[22]:=
MrvLimit [ f * ( - a ) ^ n * a ^ ( - n ) , a 0 , Direction - 1 ]
Out[22]=
( b 2 ) n / 2 d - n Gamma [ - c d b 2 + e b + n ] Gamma [ - c d + b e b 2 ]
Das ist das gleiche Ergebnis, wie im Fall n >0 . Es bleibt noch n =0 zu berechnen:
In[23]:=
Unprotect [ Sign ] ; Sign [ d ] = 1 ; Sign [ b ] = - 1 ; Sign [ n ] =. ; Protect [ Sign ] ;
In[24]:=
Unprotect [ Power ] ; b 2 = - b ; Protect [ Power ] ;
In[25]:=
MrvLimitClearCache [ ] ;
In[26]:=
MrvLimit [ f /. n 0 , a 0 , Direction - 1 ]
Out[26]=
1
Diesmal ergibt sich ein einfaches Ergebnis ohne weitere Nebenbedingungen. Das gleiche Ergebnis kommt auch heraus, wenn man in die anderen zwei Lösungen für n den Wert n =0 einsetzt.

Schließlich, als letzter Fall, bleibt b =0 zu berechnen:
In[27]:=
Unprotect [ Sign ] ; Sign [ d ] = 1 ; Sign [ b ] =. ; Protect [ Sign ] ;
In[28]:=
Unprotect [ Power ] ; b 2 =. ; Protect [ Power ] ;
In[29]:=
MrvLimitClearCache [ ] ;
In[30]:=
MrvLimit [ f /. b 0 , a 0 , Direction - 1 ]
MrvLimit :: SeriesFail : Series failed at 2 n LogGamma [ - 1 + n + d ω ] - LogGamma [ 1 ] 1 1 + LogGamma [ n + d 2 ω - e 2 - 1 ] ( - 1 1 ω ) - n ( - c 1 ω ) n / 2
Wagen wir die kühne Spekulation, dass diesmal der kompensierende Faktor ( -a) n(-ca) - n/2 ist, ausgehend von der Struktur der Funktion, an der Series scheiterte:
In[31]:=
MrvLimit [ f * ( - a ) n ( - c a ) - n / 2 /. b 0 , a 0 , Direction - 1 ]
Out[31]=
1
Richtig geraten. Der Grenzwert lautet für b =0 also:
lim a 0 + f=lim a 0 +(-a) - n(-ca) n /2=(-1) n(-c) n /2lim a 0 +a - n/2


Es ergibt sich also folgendes Gesamtbild:
lim a 0 + f=(-1) nb nlim a 0 +a - n , wenn b>0,d>0 ist, lim a 0 + f=(-1) n(-c) n /2lim a 0 +a - n/2 , wenn b=0,d>0 ist. lim a 0 + f=b nd - nPochhammer( -cdb - 2+eb - 1,n ) , wenn b<0,d>0 ist,
Das Beispiel zeigt eindrucksvoll, dass ein einfaches Vorzeichen einer Konstante dramatische Auswirkungen auf das Ergebnis einer Grenzwertaufgabe haben kann. In diesem Fall ändert sich selbst die Wachstumsklasse vollständig: Für b >0 konstant, für b 0 wechselnd 0 oder + , je nach dem Vorzeichen von n.

Trotzdem kann man mit etwas Intuition und Experimentierfreude die Aufgabe so umstellen, dass sie für MrvLimit lösbar bleibt, wodurch MrvLimit in den Händen eines geübten Mathematikers auch bei solchen Aufgaben zu einem wertvollen Werkzeug wird.
Zurück - Inhalt - Übersicht - Vorwärts