Квадратное уравнение на Аде

Я просто пришел и решил попробовать Ada. Недостатком является то, что синтаксис и функции сильно отличаются от 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(), чтобы преобразовать их в положительные. Но странно то, что он отлично работает для другого случая, что странно...


person starcorn    schedule 21.12.2010    source источник
comment
Что касается первых двух строк, я бы не стал использовать abs(), если вы уже знаете, что B отрицательно. Используйте B:=-B. Даже если компилятор умен и может встраивать вещи.   -  person DarenW    schedule 07.01.2011


Ответы (5)


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

Стандартная формула для решения 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;
person Alexandre C.    schedule 21.12.2010
comment
Хорошее объяснение! Это отличный пример того, почему стандартная формула из учебника для чего-то может быть не лучшим руководством для написания кода. - person DarenW; 07.01.2011

Учитывая ax2 + bx + c = 0 квадратичную формулу дает решения для x = (-b +/- sqrt(b2-4ac)) / 2a. Дискриминант d = b2-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;

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

person andand    schedule 21.12.2010
comment
Закрывать; все числа должны быть реальными (4.0 в первой строке, 0.0 во второй, 2.0 в третьей и четвертой). Также нет необходимости заключать условное выражение в скобки; if D <= 0.0 then. - person Simon Wright; 22.12.2010

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

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

so 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.

person Pete Kirkham    schedule 21.12.2010

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

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