Квадратное уравнение в Аде - PullRequest
10 голосов
/ 21 декабря 2010

Я просто пришел и решил попробовать Аду. Недостатком является то, что синтаксис и функция отклоняются от C ++. Поэтому мне нужно было набивать разные вещи, чтобы заставить это работать.

У меня вопрос, есть ли лучший способ сделать этот расчет, чем то, что я сделал здесь

   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;

У меня была проблема с отрицательными числами, поэтому я сделал оператор IF и использовал ABS (), чтобы превратить их в положительные. Но странная вещь в том, что он отлично работает для другого случая, что странно ...

Ответы [ 5 ]

19 голосов
/ 22 декабря 2010

Решить квадратные уравнения не так просто, как думает большинство людей.

Стандартная формула для решения a x^2 + b x + c = 0 равна

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

, но когда 4 a c << b^2, вычисления x1 включаютвычитая близкие числа, вы теряете точность, поэтому вместо этого вы используете

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

, который дает лучшее значение x1, но у которого x2 имеет ту же проблему, что и x1.

Поэтому правильный способ вычисления корней -

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

и использование x1 = q / a и x2 = c / q, что я считаю очень эффективным.Если вы хотите обработать случай, когда delta отрицателен, или сложные коэффициенты, то вы должны использовать сложную арифметику (что тоже довольно сложно, чтобы получить право).

Редактировать: С кодом Ады:

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;
2 голосов
/ 22 декабря 2010

При заданном топоре 2 + bx + c = 0 квадратичная формула дает решения для x = (-b +/- sqrt (b 2 -4ac) )) / 2а. Дискриминант d = b 2 -4ac будет положительным для вещественных корней, отрицательным для корней с ненулевым мнимым компонентом (т. Е. Не вещественным комплексным числом) и будет 0, если корень является двойной корень.

Итак, код Ады для этого будет:

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;

Примечание: я немного заржавел в Аде, но это должно быть близко к правильному синтаксису.

1 голос
/ 22 декабря 2010

Квадратичная формула равна x = ( -b +/- sqrt ( b ** 2 - 4*a*c ) ) / ( 2 * a )

Я предполагаю, что это 1.

, поэтому x = -( b/2 ) +/- sqrt ( ( ( b ** 2 ) / 4 ) - c )

Рассчитайте d = ( b ** 2 ) * 0.25 - c, затем проверьте его знак.

Если знак d отрицателен, у вас сложные корни;обращайтесь с ними как хотите.

Замена - c на + abs ( c ), если b окажется отрицательным, даст вам мусор.

Обычно умножение на 0,5 или 0,25 лучше, чем деление на2.0 или 4.0.

0 голосов
/ 12 марта 2017

Для меня вопрос больше связан с числовым алгоритмом, чем с языком Ада. Как всегда с числовыми вычислениями, часто нужно (если не всегда) ссылаться на справочные / академические документы.

Эти любопытные вопросы всегда напоминают мне об этом: https://en.wikipedia.org/wiki/Fast_inverse_square_root

Следующие приемы можно найти только в том случае, если вы «выполняете математику» или находите бумагу, которая решает вашу проблему.

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...? 
    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: как указывается в статье в википедии, эта реализация, вероятно, устарела для большинства платформ

0 голосов
/ 22 декабря 2010

Хотя я не знаю Аду, я вижу следующие вещи, которые можно оптимизировать:

  1. В первой ветви инструкции IF вы уже знаете, что B отрицательно. Таким образом, вы можете сказать B := -B вместо B := ABS(B). Или лучше: просто используйте -B там, где вы использовали B в первой ветке.
  2. Вы используете подвыражение B/2.0 четыре раза. Может быть более эффективным (а также более понятным) присвоить B/2.0 вспомогательной переменной B_2 (или назначить ее снова B, если вы не хотите тратить другую переменную) и использовать ее вместо этого.
    Также sqrt рассчитывается дважды. Присвоение его переменной auxilariy экономит время выполнения (и делает для читателя очевидным, что один и тот же подвыражение используется дважды).
  3. Чем, вероятно, будет быстрее использовать B_2*B_2 вместо **2.0; еще лучше было бы использовать выделенную функцию квадрата, если она есть в Аде.
...