2012-04-12 10 views
7

Ich habe einen Regelkreis, der mit hoher Frequenz läuft und jeden Zyklus eine Quadratwurzel berechnen muss. Typische Quadratwurzelfunktionen funktionieren gut, erfordern jedoch übermäßige Zeit. Da der Wert, den ich der Quadratwurzel nehme, sich nicht zu sehr bei jedem Zyklus ändert, würde ich gerne eine iterative Quadratwurzel finden, die konvergiert und dann das korrekte Ergebnis verfolgt. Auf diese Weise konnte ich bei jedem Zeitschritt eine einzige Iteration durchführen, anstatt viele.Verfolgungswurzelwurzel des sich bewegenden Wertes

Das Problem ist, dass alle iterativen Quadratwurzel-Methoden, die ich gesehen habe, wahrscheinlich fehlschlagen werden, wenn sich die Eingabe ändert. Insbesondere sieht es so aus, als würde es Probleme geben, wenn die Eingabe auf Null geht und dann wieder zunimmt - die Methoden möchten nicht mit einer Schätzung von Null beginnen.

Mein Eingabebereich ist 0-4,5 und ich brauche eine Genauigkeit von etwa 0,01, also könnte die Verwendung eines Inkrementes/Dekrements von 0,01 viel zu lange dauern - ich möchte, dass es in 10 Zyklen oder weniger konvergiert.

FYI Ich verwende 16/32bit Festpunkt der Eingang ist 16bit q12. Es ist auf einem Mikrocontroller, also bin ich nicht daran interessiert, 1K für eine Nachschlagetabelle zu verwenden. Der Code wird auch aus einem Simulink-Modell generiert, und ihre Tabellen-Lookup-Funktionen sind ziemlich voll mit Overhead.

Gibt es eine schöne Lösung?

+0

Ein Schuss von Halleys Methode (http://www.mathpath.org/Algor/squareroot/algor.square.root.halley.htm) sollte in Ordnung tun. Wenn Sie die Division vermeiden möchten, aktualisieren Sie stattdessen 1/sqrt (x) und verwenden Sie Newton oder Halley. –

+0

Was meinst du, der Wert ändert sich? Willst du sagen, dass du 'sqrt (x + epsilon)' wissen 'x' und' sqrt (x) 'finden willst, ohne es direkt berechnen zu müssen?Oder sagen Sie, dass das Register, das x enthält, flüchtig ist und sich in der Mitte der Berechnung (!?!) Ändern kann? –

+0

Schauen Sie sich diese 'FastSqrt'-Funktion an, die beim Spielen verwendet wird http://www.gamedev.net/topic/278840-fast-sqrt/ – ja72

Antwort

1

Sie können eine Aufnahme der Halley-Methode verwenden. Es hat kubische Konvergenz und sollte daher ganz genau sein, wenn der Wert etwas bewegt:

x_{n+1} = x_n * (x_n^2 + 3Q)/(3 x_n^2 + Q) 

Diese cubcially zu sqrt(Q) konvergiert.

Referenz: http://www.mathpath.org/Algor/squareroot/algor.square.root.halley.htm

+0

Meine Simulationen zeigen, dass dies am besten funktioniert. Es ist auch das einzige, was ich ausprobiert habe, das ziemlich nahe Null funktioniert. x_n muss zumindest in der Rückmeldung auf einen kleinen Wert begrenzt werden, um eine Division durch Null zu verhindern. – phkahler

+0

Wenn dies ein Fließkommawert ist, können Sie den Exponenten der Eingabe einfach halbieren und den resultierenden Wert als erste Schätzung verwenden, um in diesen oder einen anderen iterativen Quadratwurzelalgorithmus zu gelangen. –

+0

@R .. Anfängliche Schätzung ist der letzte aktualisierte Wert (siehe Frage). Aber ja, in einer allgemeineren Umgebung würde das sehr gut funktionieren. –

5

der Bereich 0-4,5 ist ziemlich klein. Mit einer Genauigkeit von 0,01 sind das nur 450 mögliche Berechnungen. Sie könnten sie alle zur Kompilierzeit als Konstanten berechnen und nur zur Laufzeit nachschlagen.

+0

Zu viel Speicher - die Frage wurde entsprechend bearbeitet. – phkahler

+0

Haben Sie versucht, diese Frage oder etwas Vergleichbares auf der Mathe-Stack-Tauschseite zu veröffentlichen? –

2

Ich würde vorschlagen, dass Sie eine Nachschlagetabelle verwenden, wenn Sie in den Bereichen wissen, mit denen Sie es zu tun haben. Generieren Sie eine Array- oder Hash-Tabelle (abhängig von der Sprache, in der Sie arbeiten) mit der Genauigkeitsstufe, die Sie benötigen, und beziehen Sie sich darauf, wenn Sie Ihre Roots benötigen.

2

habe ich versucht, eine zweite Ordnung Taylorentwicklung auf sqrt(x) und folgendes Ergebnis

wenn y=sqrt(x) gehe und Sie wissen y_c = sqrt(x_c) schon damals:

t = x-3*x_c; 
y = (12*x_c*x_c-t*t)/(8*y_c*y_c*y_c); 

Je größer x desto besser ist die Annäherung. Für den schlimmsten Fall mit x_c=0.01 und x=0.02 ergibt sich das Ergebnis 0.1375 gegenüber dem realen Ergebnis sqrt(0.02)=0.1414 oder ein Unterschied von 0.0039, der unter 0.01 ist.

Ich testete den Code mit C# und sah eine stetige 33% Beschleunigung vs Math.Sqrt().

+0

Ich werde das untersuchen. Die Aufteilung ist unerwünscht, kann aber OK sein. Was macht es, wenn die Eingabe für eine Weile Null ist? – phkahler

+0

@phkahler: Shortcut die Null, um '0' duh zurückzugeben! Wenn 'y_c' gleich Null ist, müssen Sie 'sqrt() 'auswerten. – ja72

+0

@phkahler: Sie werden die Division sowieso brauchen, wenn Sie 'sqrt (x)' berechnen (Sie können es vermeiden, wenn Sie 1/sqrt (x) berechnen). –