2010-12-21 11 views
9

Ich kam gerade und beschloss, etwas Ada zu versuchen. Der Nachteil ist, dass die Syntax und Funktion von C++ entfernt ist. Also musste ich verschiedene Sachen stopfen, um dieses Ding zur Arbeit zu bringen.Quadratische Gleichung in Ada

Meine Frage ist, ob es eine bessere Art und Weise sind diese Berechnung zu tun, was ich hier

IF(B < 0.0) THEN 
     B := ABS(B); 
     X1 := (B/2.0) + Sqrt((B/2.0) ** 2.0 + ABS(C)); 
     X2 := (B/2.0) - Sqrt((B/2.0) ** 2.0 + ABS(C)); 
    ELSE 
     X1 := -(B/2.0) + Sqrt((B/2.0) ** 2.0 - C); 
     X2 := -(B/2.0) - Sqrt((B/2.0) ** 2.0 - C); 
    END IF; 

ich ein Problem mit negativen Zahlen hatte getan, deshalb habe ich eine Erklärung ab und ABS verwendet IF hat() um diese positiv zu machen. Aber das Seltsame ist, dass es perfekt für den anderen Fall arbeitet, die seltsam ist ...

+1

+1 in SO – ja72

+1

In Bezug auf die ersten beiden Zeilen ADA zu erwähnen - ich vermeiden würde abs() verwenden wenn Sie bereits wissen, B ist negativ. Benutze B: = - B. Auch wenn der Compiler schlau ist und Inline-Zeug kann. – DarenW

Antwort

18

Lösen von quadratischen Gleichungen ist nicht so einfach wie die meisten Leute denken.

Die Standardformel für a x^2 + b x + c = 0 Lösung ist

delta = b^2 - 4 a c 
x1 = (-b + sqrt(delta))/(2 a) (*) 
x2 = (-b - sqrt(delta))/(2 a) 

aber wenn 4 a c << b^2, Computing x1 beinhaltet eine enge Zahlen subtrahieren, und macht Sie Genauigkeit verlieren, so verwenden Sie die folgende statt

delta as above 
x1 = 2 c/(-b - sqrt(delta))  (**) 
x2 = 2 c/(-b + sqrt(delta)) 

welche Ausbeuten ein besseres x1, aber wessen x2 das gleiche Problem hat, wie x1 oben hatte.

Der richtige Weg, um die Wurzeln zu berechnen ist daher

q = -0.5 (b + sign(b) sqrt(delta)) 

und verwenden x1 = q/a und x2 = c/q, die ich sehr effizient finden. Wenn Sie den Fall behandeln wollen, wenn delta negativ ist, oder komplexe Koeffizienten, dann müssen Sie komplexe Arithmetik verwenden (was ziemlich schwierig ist, auch richtig zu kommen).

Edit: Mit Ada Code:

DELTA := B * B - 4.0 * A * C; 

IF(B > 0.0) THEN 
    Q := -0.5 * (B + SQRT(DELTA)); 
ELSE 
    Q := -0.5 * (B - SQRT(DELTA)); 
END IF; 

X1 := Q/A; 
X2 := C/Q; 
+0

Schöne Erklärung! Dies ist ein hervorragendes Beispiel dafür, warum die Standard-Lehrbuchformel für etwas möglicherweise nicht die beste Anleitung zum Schreiben von Code ist. – DarenW

1

Die quadratische Formel x = (-b +/- sqrt (b ** 2 - 4*a*c))/(2 * a)

Ich vermute, a 1.

so x = -(b/2) +/- sqrt (((b ** 2)/4) - c)

Berechnen Sie d = (b ** 2) * 0.25 - c dann überprüfen Sie sein Zeichen.

Wenn das Zeichen d negativ ist, haben Sie komplexe Wurzeln; Handle sie wie du willst.

Ersetzen - c mit + abs (c) Wenn b geschieht negativ sein wird Ihnen Müll.

Normalerweise ist die Multiplikation mit 0,5 oder 0,25 besser als die Division durch 2,0 oder 4,0.

0

Obwohl ich nicht Ada weiß, ich sehe folgende Dinge, die optimiert werden können:

  1. Im ersten Zweig der IF instructiuon Sie bereits wissen, dass B negativ ist. So können Sie B := -B anstelle von B := ABS(B) sagen. Oder besser: verwenden Sie einfach -B, wo Sie B im ersten Zweig verwendet haben.
  2. Sie verwenden den Teilausdruck B/2.0 viermal. Es könnte effizienter (und auch klarer) sein, B/2.0 einer Hilfsvariable B_2 zuzuweisen (oder sie erneut B zuzuordnen, wenn Sie keine andere Variable ausgeben möchten) und stattdessen verwenden.
    Auch die sqrt wird zweimal berechnet. Die Zuordnung zu einer Hilfsvariablen spart die Laufzeit (und macht es für den Leser explizit, dass genau der gleiche Unterexpression zweimal verwendet wird).
  3. Dann wäre es wahrscheinlich schneller zu verwenden B_2*B_2 statt **2.0; noch besser wäre es, eine dedizierte Quadratfunktion zu verwenden, wenn es eine in Ada gibt.
2

Gegeben ax 2 + bx + c = 0 wird die quadradic formula gibt Lösungen für x = (-b +/- sqrt (b 2 -4ac))/2a. Die Diskriminante d = b -4ac ist positiv für reellwertige Wurzeln, negativ für Wurzeln mit einer von Null verschiedenen imaginären Komponente (d. H. Eine nicht-reelle komplexe Zahl) und wird 0, wenn die Wurzel eine doppelte Wurzel ist.

So würde der Ada-Code für diese sein:

D := B ** 2.0 - 4.0 * A * C; 
IF D >= 0.0 THEN 
    X1 := (-B + Sqrt(D))/(2.0 * A); 
    X2 := (-B - Sqrt(D))/(2.0 * A); 
ELSE 
    -- Deal with the fact that the result is a non-real complex number. 
END IF;

Hinweis: ich ein bisschen rostig in Ada bin, aber dies sollte auf die richtige Syntax nahe sein.

+1

Schließen; alle Zahlen müssen real sein (4.0 in der ersten Zeile, 0.0 in der zweiten, 2.0 in der dritten und vierten). Es ist auch nicht notwendig, Klammern um den Bedingungsausdruck zu setzen; 'wenn D <= 0.0 dann'. –

+0

@Simon Wright: Fest. Vielen Dank. – andand

-1

Für mich ist die Frage mehr im Zusammenhang mit numerischem Algorithmus als Ada Sprache. Wie immer bei numerischen Berechnungen, muss man oft (wenn nicht - immer -) auf Referenzdokumente verweisen.

diese irgendwie Fragen erinnert mich immer an diesem: https://en.wikipedia.org/wiki/Fast_inverse_square_root

Sie nur die folgenden Tricks finden kann, wenn man „die Mathematik tun“ oder ein Blatt Papier, die Ihre Frage Adressen.

float Q_rsqrt(float number) 
{ 
    long i; 
    float x2, y; 
    const float threehalfs = 1.5F; 

    x2 = number * 0.5F; 
    y = number; 
    i = * (long *) &y;      // evil floating point bit level hacking 
    i = 0x5f3759df - (i >> 1);    // what the fuck? 
    y = * (float *) &i; 
    y = y * (threehalfs - (x2 * y * y)); // 1st iteration 
// y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, this can be removed 

    return y; 
} 

PS: wie der wikiepdia Artikel weist darauf hin, ist diese Implementierung wahrscheinlich veraltet für die meisten Plattformen jetzt