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

4 Der MrvLimit-Algorithmus

Nachdem wir nun eine Vorstellung der Wachstumsprozesse im Unendlichen haben und bereits einige Bruchstücke des Algorithmus kennen, wird es jetzt Zeit, den MrvLimit-Algorithmus vollständig zu betrachten. Der Fokus wechselt mit diesem Kapitel weg vom mathematischen Hintergrund, hin zu den Algorithmen und Strategien.

Während wir den Ablauf des Algorithmus Schritt für Schritt beobachten, werden wir auch gleich überlegen, welche weiteren Funktionen problemlos in das Funktionsmodell übernommen werden können.

Außerdem werden wir einen kritischen Blick darauf werfen, ob die vorkommenden rekursiven Aufrufe des Algorithmus tatsächlich nicht zu unendlichen Rekursionen führen können. Die entsprechenden Nachweise sind anspruchsvoll, jedoch zum Verständnis des Algorithmus nicht zwingend erforderlich, lassen sich also beim ersten Lesen bequem überspringen.

4.1 Transformation

Wie schon in
Kapitel 2.1 dargelegt, ist der Algorithmus darauf spezialisiert, Grenzwertaufgaben für x zu lösen. Zunächst muss die Funktion also geeignet transformiert werden: Für x x 0 + ersetze x durch x 0 +1 x , für x x 0 - ersetze x durch x 0 -1 x und für x - ersetze x durch -x.

Die Behandlung von beidseitigen Grenzwerten wird in Computeralgebrasystemen unterschiedlich gehandhabt: Mathematica scheint bevorzugt Grenzwerte von oben (Direction -1) zu berechnen, Maple liefert undefined, falls der Grenzwert nicht eindeutig ist. Im reellen Fall bleibt noch die Möglichkeit, beide Grenzwerte auszugeben, falls sich unterschiedliche Grenzwerte ergeben. Abgesehen von Mathematicas unvorsichtiger Herangehensweise muss man bei beidseitigen Grenzwerten aber immer beide einseitigen Grenzwerte ermitteln und vergleichen.

4.2 Erweitertes Funktionsmodell

Der ursprüngliche Algorithmus beschränkt sich auf exp-log Funktionen, d.h. Konstanten, Potenzen von x, Grundrechenarten, Exponentialfunktion und Logarithmus. Betrachtet man den Algorithmus aber im Detail, werden hauptsächlich vier Arten von Funktionen unterschieden: Funktionen mit stetigem Grenzübergang, Funktionen mit hebbaren polynomiellen Polstellen, Funktionen mit logarithmischen Polstellen und Funktionen mit nicht hebbaren (essenziellen) Polstellen, jeweils natürlich auf den Punkt des Grenzüberganges bezogen. Grenzübergänge von stetigen Funktionen können direkt berechnet werden, hebbare Polstellen werden durch Potenzreihenanalyse betrachtet. Logarithmische Polstellen werden dabei automatisch von ω-Termen zu x-Termen abgebaut. Nur die nicht hebbaren Polstellen der Exponentialfunktion werden algorithmisch erfasst und analysiert.

Daher spricht auch nichts dagegen, weitere Funktionen in das Funktionsmodell aufzunehmen, solange die Funktionen höchstens hebbare Polstellen besitzen bzw. der Grenzübergang an unkritischen Stellen der Funktion auftritt und solange die Funktion in eine Potenzreihe entwickelbar ist.

Unter anderem kann so der Funktionsraum um trigonometrische Funktionen und die Gammafunktion erweitert werden:

sin (x),cos(x),tan(x) für x arcsin (x),arccos(x) für x arctan (x) für -x+ Γ (x) für x Γ s (x)=log(Γ(x)) für 0x+ ψ (x)=Γ'(x)/Γ(x) für -<x+ ψ ( n) (x)=d n dx n ψ(x) für -<x+
(Weitere Funktionen werden in
[Gru96] Kapitel 5.1 aufgeführt.)

Falls solche Funktionen in der Grenzwertaufgabe auftreten, muss vor der weiteren Grenzwertberechnung überprüft werden, ob das Argument der Funktion existiert und die jeweiligen Einschränkungen erfüllt werden. Das macht man am besten in Form einer Vorverarbeitung.

4.3 Vorverarbeitung

Die Vorverarbeitung dient dazu, die Struktur der Funktion zu überprüfen und anzupassen. Nicht unterstützte Funktionen müssen erkannt werden, bei manchen Funktionen muss das Argument der Funktion auf Gültigkeit geprüft werden. Der dazu erforderliche rekursive Aufruf des Grenzwertalgorithmus ist nicht kritisch, da der rekursive Aufruf ja nur mit einem Teilausdruck der Funktion erfolgt. Unendliche Rekursionen sind so nicht möglich.

Falls ein nicht unterstützter Ausdruck auftritt, muss die Grenzwertberechnung mit einer entsprechenden Fehlermeldung abgebrochen werden. Andere Ausdrücke müssen für die weitere Verarbeitung erst in eine geeignete Darstellung transformiert werden:
a b e log (a)b es sei denn, a=e oder b ist konstant. Γ (x) e Γ s (x) falls x


Gleichzeitig bietet es sich an, redundante Funktions-Schreibweisen, wie z.B. bei den Trigonometrischen Funktionen, auf die Grundformen zu beschränken:
sec (x) 1 /cos(x) csc (x) 1 /sin(x) cot (x) 1 /tan(x) arcsec (x) arccos (1/x) arccsc (x) arcsin (1/x) arccot (x) arctan (1/x)
Nach der Vorverarbeitung muss sichergestellt sein, dass die Funktion keine unerwarteten Teilausdrücke mehr enthält. Nur mit einem klar begrenzten Funktionsumfang kann der Algorithmus zuverlässig arbeiten.
Als Folge sind ab hier auch automatische Vereinfachungssysteme der Computeralgebraprogramme mit äußerster Vorsicht zu benutzen, da so schnell wieder Funktionen auftauchen, die nicht innerhalb des Modells liegen.

Ein weiterer Fall, den man besser vorab behandelt, sind konstante Funktionen, die nicht von x abhängen. Der Algorithmus scheitert an Funktionen ohne jedes Wachstum daran, das Wachstum auf Mindestniveau anzuheben. Da hier aber nichts zu berechnen ist, sollte man die weitere Verarbeitung frühzeitig abbrechen.

Schlimmer noch sind Funktionen, die in einer ganzen Umgebung um Unendlich identisch Null sind, wie zum Beispiel 1 -(x-C)/( x-C) 2 , die identisch Null ist für x >C . Solche Funktionen sind praktisch nicht sicher erkennbar, sprengen aber das Funktionsmodell des Algorithmus und führen so zu Programmfehlern und Abbrüchen.

4.4 Rekursion und Terminierung

Immer wieder kommt es im Laufe des Algorithmus zu rekursiven Aufrufen der Grenzwertberechnung. So kann eine einzige Grenzwertaufgabe durchaus eine Kaskade von mehreren Hundert weiteren Grenzwertberechnungen auslösen. Wird dabei bei der Berechnung von lim x f(x) irgendwann ein rekursiver Aufruf nötig, der wieder f (x) oder gewisse Variationen davon enthält, ist eine unendliche Schleife nicht mehr zu verhindern.

Um sicher zu gehen, dass solche Schleifen nicht auftreten, sollte bei jedem rekursiven Aufruf eine Art 'Fortschritt' bei der Bewältigung der Aufgabe erkennbar sein. In unserem Fall wird der Fortschritt messbar, indem wir in zwei Schritten die Komplexität einer Funktion berechenbar machen:

    function ComplexitySet(t : Term)
        if (x not in t)
            return {}
        if (t = x)
            return { x }
        if (t = _f_ + _g_)
            return ComplexitySet(f) ⋃ ComplexitySet(g)
        if (t = _f_ * _g_)
            return ComplexitySet(f) ⋃ ComplexitySet(g)
        if (t = _g_  _c_ and x not in c)
            return ComplexitySet(g)
        if (t = Log(_g_))
            return { Log(g) } ⋃ ComplexitySet(g)
        if (t = e  _g_)
            return { eg } ⋃ ComplexitySet(g)
        if (t = PolyGamma(_n_,_g_))
            return ComplexitySet(g)
        
        if (t = _f_(_g_))
            if (f in { Sin, Cos, Tan, ArcSin, ArcCos,
                       ArcTan, Gamma, LogGamma } )
                return ComplexitySet(g)

    end function

    function Complexity(t : Term)
        return SizeOf(ComplexitySet(t))
    end function
_t_ dient dabei als Platzhalter für beliebige Teilausdrücke, die in Folge dann als t referenziert werden. Complexity(t) wird auch kurz als C (t) bezeichnet.

Wie man leicht sieht, gilt C (e f)=C(logf)=C(f)+1 , wohingegen für die Grundrechenarten nur gilt: max (C(f),C(g))C(f+g)=C(fg)C(f)+C(g) .

Um unendliche Rekursionen sicher auszuschließen, wird die Forderung aufgestellt, dass bei jedem rekursiven Aufruf die Komplexität der rekursiv gelösten Aufgabe niedriger ist, als die Komplexität der ursprünglichen Funktion.

Leider ist auch das nicht haltbar, deshalb werden in
Kapitel 4.11 noch zwei Fälle untersucht, in denen eine rekursive Berechnung von gleicher Komplexität erforderlich sein kann. Die rekursiven Aufrufe sind jedoch von ausrechend spezieller Natur, um sicher zu stellen, dass die Komplexität in späteren rekursiven Aufrufen sinken wird.

4.5 Stärkstes Wachstum

Nun ist der Weg frei, die Funktion auf ihr grobes Wachstumsverhalten zu untersuchen. Wir gehen dabei analog zu
Kapitel 3.4 vor.

Bei MrvSet, der Menge aller Teilausdrücke der stärksten Wachstumsklasse, weichen wir jedoch von der mathematischen Sichtweise in einigen Details ab: Sehen wir uns die Funktion MrvSet im Detail an:
    function MrvSet(t : Term)
        if (x not in t)
            return {}
        if (t = x)
            return { x }
        if (t = _f_ + _g_)
            return MrvMax(MrvSet(f),MrvSet(g))
        if (t = _f_ * _g_)
            return MrvMax(MrvSet(f),MrvSet(g))
        if (t = _g_  _c_ and x not in c)
            return MrvSet(g)
        if (t = Log(_g_))
            return MrvSet(g)
        if (t = e  _g_)
            if (|Limit(g,x→inf)| < inf)
                return MrvSet(g)
            if (Limit(g,x→inf)=+/-inf)
                return MrvMax({ eg },MrvSet(g))
        if (t = PolyGamma(_n_,_g_))
            return MrvSet(g)
        
        if (t = _f_(_g_))
            if (f in { Sin, Cos, Tan, ArcSin, ArcCos,
                       ArcTan, Gamma, LogGamma } )
                return MrvSet(g)

    end function
_t_ dient dabei wieder als Platzhalter für beliebige Teilausdrücke, die in Folge dann als t referenziert werden. Die Maximum-Mengenvereinigung MrvMax ist bereits in Kapitel 3.4 beschrieben und wird gleich noch im Detail dargelegt.

Im Falle der Exponentialfunktion wird erstmals ein rekursiver Aufruf des Grenzwertalgorithmus erforderlich. Da aber der rekursive Aufruf nur mit dem Exponent erfolgt, die Komplexität also um eins sinkt, ist eine unendliche Rekursion hier nicht zu befürchten.

Die trigonometrischen und anderen Funktionen am Ende benötigen keine besondere Aufmerksamkeit mehr, da der Grenzübergang nur an unkritischen Stellen der Funktion erfolgt. Kritische Stellen wurden bereits von der Vorverarbeitung aufgelöst oder abgewiesen.

Für den MrvLimit-Algorithmus ist es von entscheidender Bedeutung zu erkennen, welcher Art die von MrvSet zurückgelieferten Mengen sind: Auf diese Eigenschaften wird später im Algorithmus aufgebaut.

Es lohnt noch anzumerken, dass die Wachstumsklasse γ (x) in der Regel, aber nicht immer, als { x} zurückgegeben wird. Andernfalls muss ein Term die Form e f (x) haben und dieses f (x) muss logarithmisch oder langsamer wachsen. Die meisten solchen Terme fallen vorher bereits Mathematicas automatischer Vereinfachung zum Opfer.

Es bleibt der Funktionsaufruf von MrvMax zu erklären. Da die beiden übergebenen Mengen jeweils nur Elemente einer Wachstumsklasse enthalten, genügt es, je einen Stellvertreter jeder Menge auszusuchen und deren Wachstum zu vergleichen:
    function MrvMax(s1,s2 : Set of Term)
        if (s1 = {})
            return s2
        if (s2 = {})
            return s1
        if (s1[1] = s2[1])
            return Union(s1,s2)

        t1 = Log(s1[1])
        t2 = Log(s2[1])
        if (t1 = Log(e_f_)
            t1 = f
        if (t2 = Log(e_f_)
            t2 = f
        if (Limit(t1/t2,x→inf) = inf)
            return s2
        if (Limit(t1/t2,x→inf) = 0)
            return s1

        return Union(s1,s2)
    end function
Der Fall der leeren Mengen (d.h. konstanten Terme) wird vorab erledigt. Danach werden die Stellvertreter beider Mengen anhand von Theorem 3.7 verglichen. Sind die Stellvertreter vom Typ e f (x) , kann log (e f (x)) sofort zu f (x) vereinfacht werden.

Da wieder rekursive Aufrufe der Limit-Funktion auftreten, muss wieder mit Vorsicht vorgegangen werden. Dazu muss beobachtet werden, in welchen Situationen MrvMax aus MrvSet heraus aufgerufen wird.

Stößt MrvSet auf einen Term der Form e g (x) , kann es zu einem Aufruf MrvMax (e g (x), MrvSet (g(x))) kommen. Das erste Argument kann dabei schlimmstenfalls die Funktion selbst sein, hat also maximal die Komplexität C (e g (x)) . Durch die Vereinfachung sinkt die Komplexität aber auf C (g(x))=C(e g (x))-1 .

Das zweite Argument hat maximal die Komplexität C (g(x)) , ist aber in jedem Fall ein Teilausdruck von g (x) . Die Komplexität des rekursiven Aufrufs ist daher begrenzt mit C (t 1/t 2)C(e g (x))-1 .

Es bleiben die Aufrufe von MrvMax bei f (x)+g(x) und f (x)g(x) . Schlimmstenfalls ist dabei die ursprüngliche Funktion direkt t =f(x)g(x) , und der Wachstumsvergleich findet tatsächlich für die Funktionen f (x) und g (x) statt. Entsprechendes gilt für f (x)+g(x) .

Der Fall f (x)=g(x)=x wird vorab behandelt und führt nicht zu einer Rekursion. Ist o.B.d.A. g (x)=x und f (x)x , so bleibt für f (x) nur die Form f (x)=e f 1 (x) , und es kommt zum rekursiven Aufruf lim x f 1 (x)/log x . Die Komplexität dieses Ausdrucks ist möglicherweise gleich der von f (x)g(x)=e f 1 (x)x . Den Beweis, dass der Grenzwertaufruf lim x f 1 (x)/log x nicht zu einer unendlichen Rekursion führt, wird auf das Kapitel 4.11 aufgeschoben.

Ist schließlich f (x)=e f 1 (x) und g (x)=e g 1 (x) , so erfolgt der rekursive Aufruf lim x f 1 (x)g 1 (x) mit einer garantiert niedrigeren Komplexität als f (x)g(x) , die Komplexität sinkt also.

4.6 Anhebung der Wachstumsklasse

Nachdem wir nun das schlimmst mögliche Wachstumsverhalten der Funktion ermittelt haben, wird es Zeit, sich eines unangenehmen Sonderfalls zu entledigen: Wachstum der Klasse γ (x) , d.h. Funktionen, die polynomiell oder langsamer wachsen.

Wie schon im
Kapitel 2.3 angeschnitten, wird solange x durch e x ersetzt, bis das Wachstum der Funktion nicht mehr in die Klasse γ (x) fällt. Danach enthält die Funktion garantiert Teilausdrücke von exponentiellem Wachstum, die klar von Teilausdrücken niedriger Ordnung getrennt werden können. Als Nebeneffekt werden durch das Anheben Terme mit logarithmischem Wachstum so weit angehoben, dass sie entweder exponentiell sind oder vorerst von anderen exponentiellen Termen überschattet werden.

Das Anheben der Wachstumsklasse ist allerdings nicht ohne Folgen. Die Komplexität der Funktion nimmt durch das Ersetzen von x durch e x zu, deswegen muss sehr vorsichtig auf eventuelle unendliche Rekursionen geachtet werden! Das MrvSet der angehobenen Funktion erneut zu berechnen, kann bereits zu einer Schleife führen. Zum Glück ist das jedoch nicht immer erforderlich. Die Komplexität wird auch in Grenzen gehalten, wenn konsequent jedes Vorkommen von log (x) nicht durch log (e x) , sondern gleich durch x ersetzt wird:

    Ω = MrvSet(f)
    ScaleUp = 0
    while (x in Ω)
        ScaleUp = ScaleUp + 1
        f = Replace(f, Log(x) → x, x → ex)
        Ω = Replace(Ω, Log(x) → x, x → ex)

        for each ω in Ω
            if (ω not in f)
                Ω=Delete(Ω,ω)
        end for

        if (Ω = {})
            Ω = MrvSet(f)
    end while
Es genügt, auf das Vorkommen von x in Ω zu achten, da das MrvSet der Klasse γ (x) immer auch x enthält. ScaleUp zählt mit, wie häufig die Funktion in eine höhere Klasse angehoben wurde, damit die Ergebnisse später wieder in die ursprüngliche Klasse abgesenkt werden können. Ist man nur am schlichten numerischen Ergebnis interessiert und nicht an Asymptoten, kann das auch eingespart werden.

Als nächstes werden in f und Ω synchron alle x und log (x) Terme durch ihre angehobenen Varianten ersetzt. Ω ist damit fast schon wieder identisch zum neuen MrvSet(f). Der einzige Fall, in dem die Ersetzung in f und in Ω unterschiedlich erfolgt, ist ein Auftreten von log (x) in f.
Tritt in f ein Term log (x) auf, so ist dessen Repräsentant in Ω der Term x. Daher wird in Ω die Ersetzung x e x angewendet, und in f wird log (x)x ersetzt. Dadurch enthält Ω ein e x , das möglicherweise nicht in f vorkommt.

Deswegen wird zunächst erst einmal jedes Element von Ω entfernt, das nicht mehr in f vorkommt. Ist Ω jetzt nicht leer, so gibt es in f mindestens einen Teilausdruck mit exponentiellem Wachstum. Alle Teilausdrücke von f mit exponentiellem Wachstum waren vorher mit polynomiellem Wachstum in Ω vertreten und wurden in Ω und f identisch transformiert. Es gilt also wieder Ω=MrvSet(f).

Ist Ω dagegen leer, so kann in f nur die Ersetzungsregel log (x)x angewendet worden sein, und f enthält immer noch keinen Term mit exponentiellem Wachstum. Da die bisherige Wachstumsklasse vollständig eliminiert wurde, muss die nächst niedrigere, bisher logarithmische Wachstumsklasse erneut ermittelt werden.

Insgesamt ist also nun folgendes passiert: Der rekursive Aufruf von MrvSet ist zum Glück unkritisch, da die Ersetzungsregel log (x)x die Komplexität von f vorher gesenkt hat. Bei den noch folgenden rekursiven Aufrufen muss jedoch mit um so mehr Sorgfalt vorgegangen werden, da mit der schlimmstenfalls um 1 gestiegenen Komplexität kalkuliert werden muss.

4.7 Wahl des Repräsentanten

Als nächstes wird ein Repräsentant ω der Menge Ω gewählt, der als Substitutionsvariable ω und Repräsentant des stärksten Wachstums dienen soll. Dabei gibt es ein später entscheidendes Kriterium zu berücksichtigen: Kein anderes Mitglied von Ω darf als Teilausdruck innerhalb von ω auftreten. Wenn also zum Beispiel Ω ={e - x,e x +e - x} ist, so ist ω =e x +e - x eine schlechte Wahl, da darin e - x auftritt. Das Problem kann umgangen werden, wenn man als ω das Element von Ω mit der geringsten Komplexität auswählt.

Der Vertreter ω muss zusätzlich die Eigenschaft lim x ω=0 erfüllen, damit später die Potenzreihenentwicklung in ω=0 durchgeführt werden kann. Da aber die Mitglieder von Ω bereits alle in der Form e g (x) vorliegen und als Grenzwert nur noch 0 oder ∞ möglich ist (s.
Kapitel 4.5), ist die Forderung ω 0 leicht zu erfüllen: Wenn lim x g(x)= , dann verwende ω =e - g(x) . Diese Vorzeichenänderung hat keine Auswirkungen auf die Stabilität des Algorithmus oder die Komplexität der Funktion.

Interessanter ist da schon die Frage, woher man weiß, ob lim x g(x)= gilt. Dafür einen rekursiven Aufruf des Grenzwertalgorithmus starten könnte zu unendlichen Rekursionen führen.

Zum Glück ist das aber nicht notwendig, denn es kommen nur zwei 'Verursacher' als Quelle für ω in Frage: Im ersten Fall ist das Wachstumsverhalten offensichtlich, im zweiten Fall kann es anhand des damals sowieso berechneten Grenzwerts von h (x) ermittelt werden. Es bietet sich daher an, entweder den Grenzwert von g (x) durch exaktes Zurückverfolgen zum Ausgangspunkt zu bestimmen, oder, besser noch, das Wachstumsverhalten von vornherein bei der Bestimmung von Ω mit zu bestimmen und aufzubewahren.

4.8 Umschreiben der Funktion

Nun wird die Funktion so umgeschrieben, dass alle Teilausdrücke des stärksten Wachstums durch ω repräsentiert werden. Den dafür nötigen Ansatz liefert
Kapitel 3.5: Ersetze jedes Vorkommen von g Ω in f durch A ω c . Nimmt man g =e s und ω =e t an, so ergibt sich die Konstante c =lim x s/t und A =e s -ct . Bei der Ersetzung sollte ω gleich als symbolische Konstante eingesetzt werden und nicht wieder durch den Term ersetzt werden, den ω repräsentiert.

Nach der Ersetzung muss sichergestellt sein, dass MrvSet (f)={ω} gilt. Um das zu garantieren, müssen alle Vorkommen von Ω in f ersetzt werden, und durch die Ersetzung dürfen keine neuen Terme eingeführt werden, die in die gleiche Wachstumsklasse fallen.

Alle Vorkommen von Elementen von Ω werden direkt ersetzt, als Quelle für neue Vorkommen kommt daher nur der Ausdruck A in Frage. A selbst hat nachweislich eine niedrigere Wachstumsklasse, es bleiben aber noch Teilausdrücke von A zu berücksichtigen. Diese bestehen wiederum aus Teilausdrücken von ω und von g Ω . Es geht also darum, wann die Elemente von Ω selbst wiederum Teilausdrücke enthalten, die ebenfalls in Ω sind.

Ordnet man die Elemente von Ω nach ihrer Komplexität, wird die Situation überschaubar: Elemente von höherer Komplexität können nur Elemente niedrigerer Komplexität als Teilausdrücke enthalten. Ersetzt man nun zuerst die Elemente hoher Komplexität, so werden in den nachfolgenden Ersetzungen ebenfalls alle neu eingeführten kritischen Teilausdrücke niedrigerer Komplexität mit ersetzt. Hat man ω als das Element mit niedrigster Komplexität gewählt, so kann auch der von ω abstammende Teil in A keine neuen kritischen Teilausdrücke mehr einführen, was andernfalls zu rekursiven Ersetzungsschleifen führen könnte.

Eine weitere Quelle von Problemen sind Term-Optimierungen. Vereinfacht man A =e s -ct unvorsichtigerweise zu A =e se - ct , ist man fast wieder am Ausgangspunkt angekommen, und der Algorithmus scheitert. Je nach Computeralgebrasystem muss man daher sicher stellen, dass auf automatische Optimierungen weitgehend verzichtet wird.

Schließlich bleibt der rekursive Aufruf des Grenzwertalgorithmus. Die Gefahr einer unendlichen Rekursion ist auch hier wieder gegeben. Im Kapitel 4.12 wird ein Trick aufgezeigt, mit dem der rekursive Aufruf komplett vermieden werden kann.

Da diese Arbeit nicht auf diesen Trick zurück greift, hier der Beweis, dass auch der rekursive Aufruf terminiert: Berechnet wird der Grenzwert c =lim x s/t , ausgehend von den Ausdrücken e s und e t bzw. e - t aus Ω und damit Teilausdrücken von f. Falls die Wachstumsklasse nicht angehoben wurde, treten e s und e ± t direkt im ursprünglichen f auf, und s /t hat eine um mindestens 1 niedrigere Komplexität.

Es bleibt der Fall, dass mindestens ein mal die Wachstumsklasse angehoben wurde. Solange dabei nur log xx ersetzt wurde, kann die Komplexität höchstens gesunken sein. Beim letzten Anheben wurde aber genau ein mal x e x ersetzt, wodurch die Komplexität wieder um eins stieg.

Da nun auch garantiert e x Ω ist, liegt t =-x bereits fest, und es gilt C (s)=C(s/t) , was schlimmstenfalls gleich der Komplexität der ursprünglichen Funktion f ist.

Außerdem muss e x in e s enthalten sein, d.h. entweder ist s =x , oder e x ist in s enthalten. Im ersten Fall ergibt sich (bei geeigneter Optimierung) s /t =x /(-x)=- 1 , wodurch keine rekursiven Probleme entstehen. Andernfalls bleibt ein Grenzwertaufruf lim x s/- x , wobei s den Teilausdruck e x enthält. Dass dieser Aufruf nicht zu einer unendlichen Rekursion führt, wird im Kapitel 4.11 gezeigt.

4.9 Potenzreihe

Die Hauptarbeit des Algorithmus ist damit geschafft. Die Funktion wurde in eine Darstellung überführt, bei der die Teilausdrücke größter Wachstumsklasse nur in der Gestalt des Symbols ω auftreten, und alle anderen auftretenden Terme einer niedrigeren Klasse angehören.

ω hat die nötige Eigenschaft ω 0 für x , also können die Anfangsterme einer generalisierten Potenzreihe für f in ω =0 + ermittelt werden. Das "Wie" würde den Rahmen dieser Diplomarbeit endgültig sprengen und sei daher anderen überlassen. So viel sei gesagt, es gibt Algorithmen, die in unserem Fall die abgebrochene Potenzreihe garantiert finden.

Der Algorithmus sollte eine abgebrochene Potenzreihendarstellung für f liefern, mit der Gestalt f =a 0ω e 0 +O[ω e 1 ] , mit einem reellen Exponenten e 0 <e 1 und einem Faktor a 0 0 , der konstant oder von niedrigerer Wachstumsklasse als ω ist und der eine niedrigere Komplexität als die ursprüngliche Funktion f hat.

Ein Hinweis muss der Potenzreihenentwicklung noch auf den Weg gegeben werden. Falls im Laufe der Potenzreihenentwicklung die Singularitäten log ω oder log 1/ω auftreten, so können diese direkt vereinfacht werden: Sie gehören einer niedrigeren Wachstumsklasse an und liefern keinen Beitrag zu dieser Potenzreihe. Da ω immer die Form ω =e t hat, ist es ein leichtes, die Beziehungen log ω=t und log 1/ω=- t aufzustellen und die auftretenden Singularitäten aufzulösen.

Umgekehrt kann es je nach Algorithmus vorkommen, dass Terme der Form ω oder 1 /ω in ihrer nach x aufgelösten Form auftreten. Diese sollten dann von ihrer Darstellung mittels x zurück transformiert werden zur Darstellung mit ω, damit der Algorithmus sie bei der Potenzreihenentwicklung korrekt berücksichtigt.

4.10 Ergebnisanalyse

Es bleibt nur noch wenig zu tun. Als nächstes sollte die Anhebung der Wachstumsklasse aus
Kapitel 4.6 rückgängig gemacht werden, indem genau so oft wie damals die umgekehrte Ersetzung e x x , x logx auf ω und auf a 0 angewendet wird. Wie schon gesagt, falls man nur an dem numerischen Ergebnis interessiert ist, kann dieser Schritt auch entfallen.

Nach der Potenzreihenentwicklung sind die Wachstumskomponenten der Klasse γ (ω) konzentriert in Termen ω e k mit e k 0 , alle Komponenten niedrigerer Wachstumsklassen konzentrieren sich auf die Faktoren a k . Daher gilt: Ist e 0 >0 , so dominiert ω 0 alle anderen Terme, der Grenzwert ist also 0. Ist e 0 <0 , so dominiert 1 /ω alle anderen Terme, das Ergebnis ist Sign (lim x a 0) . Ist hingegen e 0 =0 , so heben sich alle Wachstumskomponenten der Klasse γ (ω) für x gegenseitig auf, und der Grenzwert ist lim x a 0 .

Eventuell ist also ein rekursiver Grenzwertaufruf für lim x a 0 erforderlich. Da aber die Komplexität durch die Potenzreihenentwicklung gesunken ist, bereitet der rekursive Aufruf in der nächst niedrigeren Wachstumsklasse keine Probleme. Außerdem wird durch jede Potenzreihenentwicklung eine vollständige Wachstumsklasse abgebaut, so lange, bis alle Wachstumsklassen aufgelöst sind und a 0 nicht mehr von x abhängt. Da der ursprüngliche Funktionsausdruck endliche Komplexität hat, können nur endlich viele Wachstumsklassen auftreten, der Algorithmus terminiert also.

Als zusätzliches Bonbon kann der Algorithmus auch eine Asymptote zur Funktion bestimmen, die alle Wachstumsklassen geordnet aufführt: Protokolliert man die Ergebnisse der Potenzreihenentwicklungen a ( 0,i) ω i e ( 0,i) , so ergibt sich am Ende folgende Asymptote:
A (x)=a ( 0,n)ω n -1 e ( 0,n-1) ω n -2 e ( 0,n-2) ⋅ ⋅ ⋅ω 1 e ( 0,1)
Alle ω i streben gegen Null, sind positiv, haben für zunehmendes i eine fallende Wachstumsklasse, und der erste Term mit e ( 0,i) 0 ist dominant. Außerdem gilt lim x f(x)/A(x)=1 . Der Algorithmus kann auch solange fortgesetzt werden, dass a ( 0,n) immer konstant ist. War der ursprüngliche Grenzwert nicht von der Form x , so muss natürlich auch hier noch eine Rücktransformation durchgeführt werden.

4.11 Noch einmal Terminierung

Zwischenzeitlich hatten wir in zwei Fällen einen rekursiven Aufruf zugelassen, obwohl die Komplexität des rekursiven Aufrufs schlimmstenfalls gleich der der ursprünglichen Funktion war. Um trotzdem sicher zu stellen, dass es nicht zu einer unendlichen Rekursion kommen kann, müssen wir nun zeigen, dass die nächsten rekursiven Aufrufe nicht wieder auf eine solche Ausnahme hinaus laufen.

Im ersten Fall aus
Kapitel 4.5 gilt es zu beweisen, dass lim x f 1 (x)/log x sicher terminiert, wobei dieser Grenzwert bei der Bestimmung des MrvSet von f (x)g(x) entstand mit e f 1 (x) MrvSet(f(x)) und x MrvSet(g(x)) .

Beim rekursiven Aufruf kommt es bei der Bestimmung des MrvSet wieder zu einer solchen Situation, bei der MrvSet (f 1(x)) mit MrvSet (logx)={x} verglichen wird. Stellt sich dabei heraus, dass e f 2 (x) MrvSet(f 1(x)) ist, kommt es erneut zu einem rekursiven Aufruf ähnlicher Art. In diesem Aufruf wird aber durch den Wegfall des Logarithmus die Komplexität um eins sinken, der nächste rekursive Aufruf findet also wieder mit verringerter Komplexität statt.

Im zweiten Fall aus Kapitel 4.8 geht es um den Grenzwert c =lim x s(e x)/- x . Er entstand aus der Umschreibung eines Terms e s (e x) in die Form A (e - x) c . Durch die Anhebung der Wachstumsklasse war für problemlose rekursive Aufrufe mindestens C (s(e x)/- x)<C(e s (x)) erforderlich.

Bei der MrvSet-Bestimmung wird diesmal jedoch der Teilausdruck e x sofort dominant hervortreten, eine weitere Anhebung der Wachstumsklasse ist nicht erforderlich. Dadurch ist eine direkte Schleife hier ebenfalls nicht möglich.

Eine Kopplung der beiden Ausnahmen ist noch denkbar, wenn Terme gleicher Komplexität abwechselnd durch die erste Ausnahme und die zweite Ausnahme in tiefere rekursive Aufrufe gelangen. Um auch dies auszuschließen, muss der Aufruf s (e x)/- x der zweiten Ausnahme in den MrvSet-Aufruf der ersten Ausnahme verfolgt werden. Tatsächlich kann es zu einem weiteren rekursiven Aufruf gleicher Komplexität kommen, wenn s (e x) auch darstellbar ist als e s 1 (x) , so dass es beim MrvSet zum problematischen Aufruf mit s 1 (x)/log x kommen kann. Die Schleife wird durchbrochen, wenn s 1 (x) noch immer e x enthält, da so keine Anhebung der Wachstumsklasse erforderlich ist, und es nicht wieder zu Ausnahme 2 kommen kann. Andernfalls muss s 1 (x)=x sein, der rekursive Aufruf in MrvSet ist damit x /logx , was zur angehobenen Form e x /x führt. ω wird als e - x gewählt, und der einzige bezüglich Ausnahme 2 interessante Grenzwertaufruf ist c =lim x x/- x , der aber schon im Kapitel 4.8 als unkritisch erkannt wurde.

4.12 Alternativer Ansatz

Bei der Konstante c aus
Kapitel 4.8 handelt es sich um das Wachstumsverhältnis R (g,ω) aus Definition 3.6. Berechnet wurden solche Verhältnisse bereits bei der Konstruktion von Ω mittels MrvMax. Bewahrt man diese Ergebnisse zusammen mit Ω auf, so kann daraus das Wachstumsverhältnis aller Terme in Ω relativ zu ω bestimmt werden.

Am einfachsten verfährt man dabei so: Die erste Funktion ω =Ω[1] jedes MrvSet dient als Referenz und ihr wird die Zahl r=1 zugeordnet. Wann immer eine Funktion g mittels MrvMax in die Menge aufgenommen wird, wird dessen relatives Wachstumsverhältnis r =R(g,ω) für den Klassenvergleich bereits berechnet und kann dann zusammen mit der Funktion g im MrvSet aufbewahrt werden.

Soll eine andere Funktion ω 2 die erste Funktion ω ersetzen, so sind alle gespeicherten Verhältnisse anzupassen mit Hilfe der Regel r neu =r alt R(ω,ω 2) . Wie schon im Anschluss an Theorem 3.7 gesehen, gilt ja R (g,ω 2)=R(g,ω)R(ω,ω 2) . Normalerweise ist auch dieses Wachstumsverhältnis bekannt, ein weiterer Grenzwertaufruf ist nicht nötig.

Entsprechend muss im Fall der Vereinigung bei MrvMax eine der beiden Mengen an die Referenz der anderen Menge angepasst werden, indem zum Beispiel in Ω 2 jedes Wachstumsverhältnis mit R (Ω 2[1],Ω 1[1]) multipliziert wird.

Die Anhebung der Wachstumsklasse hat auf das Wachstumsverhältnis genauso wenig einen Einfluss, wie auf den Grenzwert der Funktion selbst, hier ist also nichts zu beachten. Schließlich bleibt noch die endgültige Wahl von ω. Diese ist genauso zu behandeln, wie auch die bisherigen Wechsel der Referenzfunktion. Sollte das Wachstumsverhalten von ω durch Ersetzung ω 1/ω gedreht worden sein, ergibt sich daraus eine weitere Multiplikation für alle Wachstumsverhältnisse mit -1.

Der Lohn der Mühe ist, dass bei der Ersetzung im Kapitel 4.8 das Wachstumsverhältnis c bereits bekannt ist und nicht durch einen weiteren rekursiven Aufruf ermittelt werden muss. Dadurch treten in der Hauptschleife des Algorithmus rekursive Aufrufe nur noch bei der Bestimmung von Ω sowie bei der Parameterprüfung am Anfang auf. Insbesondere treten aber nach der Anhebung der Wachstumsklasse keine rekursiven Aufrufe mehr auf, was die Beweisführung weiter vereinfacht.

In der weiteren Diplomarbeit wird dieser alternative Ansatz jedoch nicht impementiert.
Zurück - Inhalt - Übersicht - Vorwärts