2010-12-21 23 views
9

Acabo de dar la vuelta y decidí probar un poco de Ada. El inconveniente es que la sintaxis y la función se alejan bastante de C++. Así que tuve que incluir varias cosas para hacer que esto funcione.Ecuación cuadrática en Ada

Mi pregunta es si hay alguna forma mejor de hacer este cálculo que lo que he hecho aquí

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; 

que tenía algún problema con los números negativos, por eso me hice una declaración IF y solía ABS() para convertirlos en positivos. Pero lo más extraño es que funciona perfectamente para el otro caso, lo cual es extraño ...

+1

1 por mencionar ADA en la – ja72

+1

En cuanto a las dos primeras líneas - yo evitaría el uso de abs() cuando ya sabes que B es negativo. Use B: = - B. Incluso si el compilador es inteligente y puede en línea cosas. – DarenW

Respuesta

18

Resolver ecuaciones cuadráticas no es tan simple como la mayoría de la gente piensa.

La fórmula estándar para la resolución de a x^2 + b x + c = 0 es

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

pero cuando 4 a c << b^2, computación x1 implica restar números cercanos, y hace perder precisión, por lo que utilizar la siguiente vez

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

que los rendimientos un x1 mejor, pero cuyo x2 tiene el mismo problema que x1 tenía anteriormente.

La forma correcta de calcular las raíces es, por tanto,

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

y utilizar x1 = q/a y x2 = c/q, que me parece muy eficiente. Si desea manejar el caso cuando delta es negativo, o coeficientes complejos, entonces debe usar aritmética compleja (que es bastante complicado de entender también).

Editar: Con el código Ada:

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

¡Buena explicación! Este es un excelente ejemplo de por qué la fórmula de libro de texto estándar para algo puede no ser la mejor guía para escribir código. – DarenW

1

La fórmula cuadrática es x = (-b +/- sqrt (b ** 2 - 4*a*c))/(2 * a)

supongo que a es 1.

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

Calcula d = (b ** 2) * 0.25 - c luego revisa su signo.

Si el signo de d es negativo, tiene raíces complejas; manejarlos como lo desee.

Reemplazando - c con + abs (c) si b pasa a ser negativo le dará basura.

Generalmente multiplicar por 0.5 o 0.25 es mejor que dividir por 2.0 o 4.0.

0

Aunque no sé Ada, veo siguientes cosas que pueden ser optimizados:

  1. En la primera rama de la IF instructiuon ya sabe que B es negativo. Por lo tanto, puede decir B := -B en lugar de B := ABS(B). O mejor: solo use -B donde usó B en la primera rama.
  2. Está utilizando la subexpresión B/2.0 cuatro veces. Podría ser más eficiente (y también más claro) asignar B/2.0 a una variable auxiliar B_2 (o asignarla a B nuevamente si no desea gastar otra variable) y usarla en su lugar.
    También el sqrt se calcula dos veces. Asignarlo a una variable auxiliar guarda el tiempo de ejecución (y lo hace explicito para el lector que exactamente la misma subexpresión se usa dos veces).
  3. De lo que probablemente sería más rápido usar B_2*B_2 en lugar de **2.0; aún mejor sería usar una función cuadrada dedicada, si hay una en Ada.
2

hacha Dada + bx + c = 0 la quadradic formula da soluciones para x = (-b +/- sqrt (b -4ac))/2a. El discriminante d = b -4ac será positivo para raíces con valores reales, negativo para raíces con un componente imaginario distinto de cero (es decir, un número complejo no real) y será 0 cuando la raíz sea una raíz doble.

Por lo tanto, el código Ada para esto sería:

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;

Nota: estoy un poco oxidado en Ada, pero esto debe estar cerca de la sintaxis correcta.

+1

Cerrar; todos los números deberán ser reales (4.0 en la primera línea, 0.0 en la segunda, 2.0 en la tercera y cuarta). Además, no es necesario poner corchetes alrededor de la expresión condicional; 'si D <= 0.0 luego'. –

+0

@Simon Wright: Reparado. Gracias. – andand

-1

Para mí, la cuestión está más relacionada con el algoritmo numérico que al lenguaje Ada. Como siempre ocurre con la computación numérica, a menudo (si no es así, siempre) remítase a documentos académicos o de referencia.

Estas preguntas alguna manera siempre me recuerda esto: https://en.wikipedia.org/wiki/Fast_inverse_square_root

sólo se puede encontrar los siguientes trucos si "hace la matemáticas" o encontrar un poco de papel que adresses su problema.

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; 
} 

PD: como el artículo wikiepdia señala, esta implementación es probablemente obsoleto para la mayoría de plataformas ahora