2014-04-15 4 views
5

Ich muss die Funktion in C++ kodieren, die Koeffizienten der Taylor-Reihe der gegebenen rationalen Funktion (P (x)/Q (x)) effizient findet.Bester Algorithmus für die Reihenentwicklung der Rational-Funktion

Funktionsparameter sind die Potenz von Polynomen (gleich im Nenner und Nenner), zwei Arrays mit Koeffizienten von Polynomen und die Anzahl der Terme in der Expansion.

Meine Idee folgte. Betrachten Identität

P(x)/Q(x) = R(x) + ... 

Wo R(x) ein Polynom mit Anzahl von Begriffen gleich Anzahl der Koeffizienten wir finden müssen, ist. Dann kann ich beide Seiten mit Q(x) multiplizieren und

P(x) = R(x) * Q(x) 

R(x) * Q(x) - P(x) = 0 

Daher erhalten alle Koeffizienten Null sein sollte. Dies ist ein Gleichungssystem, das O (n^3) Algorithmus zu lösen hat. O (n^3) ist nicht so schnell wie ich wollte.

Gibt es einen schnelleren Algorithmus?

Ich weiß, dass Koeffizienten der Reihen lineare Rezidivbeziehung erfüllen. Das lässt mich denken, dass O (n) Algorithmus möglich ist.

+0

@ DavidEisenstat Danke, das vereinfacht die Aufgabe. Aber wie kann ich '1/Q (x)' mit langer Teilung finden? Ich dachte bei der Division finde ich nur Quotient und Rest. – Somnium

+0

@DavidEisenstat Vielen Dank! Die Vermeidung von Trennung wird auch die Dinge beschleunigen, weil die Multiplikation * O (n * m) * ist, wobei n - Anzahl der Terme, m - Grad. Übrigens verstehe ich nicht, warum meine Antwort als zu breit markiert ist, sondern einen bestimmten Algorithmus. – Somnium

+0

@DavidEisenstat Da gehen Sie :) Vielleicht sollten wir ein Thema über den Zustand von [Algorithmus] auf Meta starten. Es gibt definitiv einige allgemeine Entscheidungen, die getroffen werden müssen, um diese Fragen an CS –

Antwort

5

Der Algorithmus, den ich beschreiben werde, wird mathematisch von formal power series gerechtfertigt. Jede Funktion mit einer Taylor-Serie hat eine formale Potenzreihe. Das Gegenteil ist nicht richtig, aber wenn wir Funktionen mit Taylor-Reihen arithmetisch ausführen und eine Funktion mit einer Taylor-Reihe erhalten, dann können wir die gleiche Arithmetik mit formalen Potenzreihen durchführen und die gleiche Antwort erhalten.

Der Algorithmus der langen Division für formale Potenzreihen ist wie der long division Algorithmus, den Sie vielleicht in der Schule gelernt haben. Ich werde es am Beispiel (1 + 2 x)/(1 - x - x^2) demonstrieren, das Koeffizienten hat, die gleich Lucas numbers sind.

Der Nenner muss eine Konstante ungleich Null haben. Wir beginnen mit dem Schreiben des Zählers, der der erste Rest ist.

   -------- 
1 - x - x^2) 1 + 2 x 

[ teilen wir die niedrigste Ordnung der Restlaufzeit (1) durch den konstanten Term des Nenners (1) und stellen die Quotienten bis oben.

   1 
      -------- 
1 - x - x^2) 1 + 2 x 

Jetzt multiplizieren wir 1 - x - x^2 von 1 und es aus dem aktuellen Rest subtrahieren.

   1 
      -------- 
1 - x - x^2) 1 + 2 x 
       1 - x - x^2 
       ------------- 
        3 x + x^2 

Wiederholen Sie den Vorgang.

   1 + 3 x 
      -------- 
1 - x - x^2) 1 + 2 x 
       1 - x - x^2 
       --------------- 
        3 x + x^2 
        3 x - 3 x^2 - 3 x^3 
        ------------------- 
         4 x^2 + 3 x^3 

Und wieder.

   1 + 3 x + 4 x^2 
      ---------------- 
1 - x - x^2) 1 + 2 x 
       1 - x - x^2 
       --------------- 
        3 x + x^2 
        3 x - 3 x^2 - 3 x^3 
        ------------------- 
         4 x^2 + 3 x^3 
         4 x^2 - 4 x^3 - 4 x^4 
         --------------------- 
           7 x^3 + 4 x^4 

Und wieder.

   1 + 3 x + 4 x^2 + 7 x^3 
      ------------------------ 
1 - x - x^2) 1 + 2 x 
       1 - x - x^2 
       --------------- 
        3 x + x^2 
        3 x - 3 x^2 - 3 x^3 
        ------------------- 
         4 x^2 + 3 x^3 
         4 x^2 - 4 x^3 - 4 x^4 
         --------------------- 
           7 x^3 + 4 x^4 
           7 x^3 - 7 x^4 - 7 x^4 
           --------------------- 
             11 x^4 + 7 x^5 

Die einzelnen Divisionen waren ziemlich langweilig, weil ich einen Divisor mit einem führenden 1 verwendet, aber wenn ich gebraucht hatte, sagen wir, 2 - 2 x - 2 x^2, dann würden alle Koeffizienten in dem Quotienten von 2 aufgeteilt werden.

+0

Danke, das hilft sehr! Alle Erweiterungen, die ich finden wollte, sind mit ganzzahligen Koeffizienten, dies garantiert wahrscheinlich, dass der führende Koeffizient 1 ist. – Somnium

1

Wenn Sie sich das System, das Sie mit Ihrem Plan erhalten, genau anschauen, können Sie sehen, dass es bereits diagonal ist und nicht erfordert, dass O (n^3) gelöst wird. Es degeneriert einfach in eine lineare Rekursion (P [], Q [] und R [] die Koeffizienten der entsprechenden Polynome ist):

R[0] = P[0]/Q[0] 
R[n] = (P[n] - sum{0..n-1}(R[i] * Q[n-i]))/Q[0] 

Da Q ein Polynom ist, wobei die Summe hat nicht mehr als deg(Q) Begriffe (somit wird die Zeit für die Berechnung konstant gehalten), wodurch die Gesamtkomplexität asymptotisch linear wird. Sie können auch die Matrixdarstellung der Rekursion für eine (möglicherweise) bessere asymptotische aussehen.

+0

Er ist linear für einen R (i) -Wert, jedoch für alle Werte R (0), ..., R (n) es wird * O (n^2) * – Somnium

+0

Nein. Es würde O (n^2) werden, wenn Q unendlich viele Koeffizienten hätte. Die Summe hat keine Terme mehr als Grad (Q), es braucht also konstante Zeit für die Berechnung. Ich werde meine Antwort bearbeiten - es ist unklar. Vielen Dank. – user58697

+0

Dann wird es O (n * q), wo q - Grad von Q. Scheint das gleiche wie in der angenommenen Post. – Somnium

1

Dies kann in O(n log n) Zeit für beliebige P und Q von Grad n durchgeführt werden. Genauer gesagt kann dies in M(n) erfolgen, wobei M(n) die Komplexität der Polynom-Multiplikation ist, die selbst in O(n log n) gemacht werden kann.

Erstens, die ersten n Begriffe einer Reihenentwicklung kann einfach als ein Polynom Grad n-1 angesehen werden.

Angenommen, Sie interessieren sich für die ersten n Begriffe der Serie Erweiterung von P(x)/Q(x). Es existiert ein Algorithmus, der das Inverse von Q in M(n) Zeit wie oben definiert berechnen wird.

Inverse T(x) von Q(x) erfüllt T(x) * Q(x) = 1 + O(x^N). I.e. T(x) * Q(x) ist genau 1 plus einige Fehler Begriff, deren Coficients alle kommen nach den ersten n Begriffe, die wir interessieren, so können wir sie einfach fallen lassen.

Jetzt P(x)/Q(x) ist einfach P(x) * T(x) das ist nur eine weitere Polynom Multiplikation.

Sie können eine Implementierung finden, die das zuvor erwähnte Inverse in meiner Open-Source-Bibliothek Altruct berechnet. Siehe die Datei series.h. Angenommen, Sie haben bereits eine Methode, die das Produkt von zwei Polyinomen berechnet, ist der Code, der die Umkehrung berechnet, etwa 10 Zeilen lang (eine Variante von Divide-and-Conquer). Der tatsächliche Algorithmus ist wie folgt: Angenommen Q(x) = 1 + a1*x + a2*x^2 + .... Wenn a0 nicht 1 ist, können Sie einfach Q(x) und später dessen inverse mit a0 teilen. Nehmen Sie an, dass Sie bei jedem Schritt L Begriffe der umgekehrten haben, so dass Q(x) * T_L(x) = 1 + x^L * E_L(x) für einen Fehler E_L(x). Anfangs T_1(X) = 1. Wenn Sie dies oben einstecken, erhalten Sie Q(x) * T_1(x) = Q(x) = 1 + x^1 * E_1(x) für einige E_1(x), was bedeutet, dass dies für L=1 gilt. Lassen Sie uns jetzt bei jedem Schritt L verdoppeln. Sie können E_L(x) aus dem vorherigen Schritt als E_L(x) = (Q(x) * T_L(x) - 1)/x^L erhalten, oder implementieren Sie einfach die ersten L Koeffizienten des Produkts. Sie können dann T_2L(x) aus dem vorherigen Schritt als T_2L(x) = T_L(x) - x^L * E_L(x) * T_L(x) berechnen. Der Fehler wird E_2L(x) = - E_L(x)^2 sein.Prüfen wir nun, ob der Induktionsschritt gilt.

Q(x) * T_2L(x) 
= Q(x) * (T_L(x) - x^L * E_L(x) * T_L(x)) 
= Q(x) * T_L(x) * (1 - x^L * E_L(x)) 
= (1 + x^L * E_L(x)) * (1 - x^L * E_L(x)) 
= 1^2 - (x^L * E_L(x))^2 
= 1 + x^2L * E_2L(x) 

Q.E.D.

Ich bin ziemlich sicher, dass es nicht möglich ist, Polynomdivision effizienter als Multiplikation zu berechnen, und wie Sie in der folgenden Tabelle sehen können, dieser Algorithmus nur 3-mal langsamer als eine einzige Multiplikation ist:

n  mul  inv  factor 
10^4  24 ms  80 ms 3,33x 
10^5  318 ms  950 ms 2,99x 
10^6 4.162 ms 12.258 ms 2,95x 
10^7 101.119 ms 294.894 ms 2,92x