Квадратное уравнение? Да раз плюнуть!

в 18:24, , рубрики: КодоБред, математика, подводные камни, метки: ,

Бытует мнение, что только 10% программистов способны написать двоичный поиск. Это мнение мы испытывать не будем, но что насчёт квадратного уравнения?
Квадратное уравнение? Да раз плюнуть!
Поставим задачу конкретнее: решение квадратного уравнения вида ax2+bx+c=0 с целочисленными коэффициентами. На вход подаются три целых числа в рамках int (коэффициенты a, b и c). Программа должна всегда выдавать результат.
Казалось бы, плёвое дело: пять минут и готово! И вот спустя те самые пять минут имеем на выходе следующий код:

Код

int a, b, c;
std::cin >> a >> b >> c;

long long discriminant = b*b - 4*a*c;

if (discriminant > 0)
  std::cout << "D > 0; 2 корня:n"
            << "x1 = " << (-b + sqrt(discriminant))/(2*a) << "n"
            << "x2 = " << (-b - sqrt(discriminant))/(2*a) << std::endl;
else if (discriminant == 0)
  std::cout << "D = 0; 1 корень:n"
            << "x = " << -1.*b/(2*a) << std::endl;
else if (discriminant < 0)
  std::cout << "D < 0; нет корней." << std::endl;

Всё здорово? Как бы не так. «Заказчик», пятиклассник Петя, недоволен: на его ввод программа выдала какие-то «nan» и «inf».

0 -1 -1
D > 0; 2 корня:
x1 = inf
x2 = nan

Пожимаем плечами, добавляем условие для вырожденного случая:

Код

if (a == 0)
{
  if (b != 0)
    std::cout << "a = 0; уравнение вырождается в линейное:n"
              << "x = " << -1.*c/b << std::endl;
  else if (c == 0)
    std::cout << "Все коэффициенты равны нулю; x - любое число." << std::endl;
  else
    std::cout << "Нет решений." << std::endl;
}
else
{
  // Предыдущий код, начиная с дискриминанта.
}

Стало лучше? А вот и нет.

1 0 -2
D > 0; 2 корня:
x1 = 1.41421
x2 = -1.41421

49 7 -2
D > 0; 2 корня:
x1 = 0.142857
x2 = -0.285714

За такие ответы Петю в школе отругали: корень должен оставаться корнем, дробь — дробью. Чешем в затылке, вздыхаем и берёмся за код; структура за структурой, функция за функцией он распухает раз в десять, но основная функция при этом остаётся относительно неизменной, хотя и сильно прибавила в весе.

Код

Весь код этого этапа.

if (a == 0)
{
  if (b != 0)
    std::cout << "a = 0; уравнение вырождается в линейное:n"
              << "x = " << fraction(-c,b).toString() << std::endl;
  else if (c == 0)
    std::cout << "Все коэффициенты равны нулю; x - любое число." << std::endl;
  else
    std::cout << "Нет решений." << std::endl;
}
else
{
  long long discriminant = b*b - 4*a*c;

  if (discriminant > 0)
  {
    std::cout << "D > 0; 2 корня:n";

    radical dRoot (discriminant);

    if (dRoot.isInteger())
    {
      std::cout << "x1 = " << fraction(-b + sqrt(discriminant), 2*a).toString() << "n"
                << "x2 = " << fraction(-b - sqrt(discriminant), 2*a).toString() << std::endl;
    }
    else
    {
      std::string rational = fraction(-b, 2*a).toString(),
                  irrational = fraction(radical(discriminant), 2*a).toString();
      if (rational == "0")
        std::cout << "x1 = " << irrational << "n"
                  << "x2 = " << "- " << irrational << std::endl;
      else
        std::cout << "x1 = " << rational << " + " << irrational << "n"
                  << "x2 = " << rational << " - " << irrational << std::endl;
    }
  }
  else if (discriminant == 0)
    std::cout << "D = 0; 1 корень:n"
              << "x = " << fraction(-b, 2*a).toString() << std::endl;
  else if (discriminant < 0)
    std::cout << "D < 0; нет корней." << std::endl;
}
1 0 -2
D > 0; 2 корня:
x1 = ┐/2
x2 = - ┐/2

49 7 -2
D > 0; 2 корня:
x1 = 1/7
x2 = -2/7

Петя доволен, Петя принёс из школы пятёрку по математике родителям и шоколадку нам.
Прошло пять лет. Пётр перешёл в десятый класс. Там на уроке алгебры ему поведали о мнимых и комплексных числах. Откопав где-то на диске нашу программу, он пытается с её помощью — негодяй! — сделать домашнее задание. Но что это?

1 0 25
D < 0; нет корней.

Негодующий Пётр приносит нам программу на доделку. Спустя пару часов чтения старого кода понимаем, что проблема решается… добавлением пары строк (честно говоря, я сам не ожидал).

Код

if (discriminant != 0)
{
  std::cout << "D != 0; 2 корня:n";

  radical dRoot (discriminant);

  if (dRoot.isInteger())
  {
    std::cout << "x1 = " << fraction(-b + sqrt(discriminant), 2*a).toString() << "n"
              << "x2 = " << fraction(-b - sqrt(discriminant), 2*a).toString() << std::endl;
  }
  else
  {
    std::string rational = fraction(-b, 2*a).toString(),
                irrational = fraction(radical(discriminant), 2*a).toString();
    if (rational == "0")
      std::cout << "x1 = " << irrational << "n"
                << "x2 = " << "- " << irrational << std::endl;
    else
      std::cout << "x1 = " << rational << " + " << irrational << "n"
                << "x2 = " << rational << " - " << irrational << std::endl;
  }
}
else if (discriminant == 0)
  std::cout << "D = 0; 1 корень:n"
            << "x = " << fraction(-b, 2*a).toString() << std::endl;

1 0 25
D != 0; 2 корня:
x1 = i*5
x2 = - i*5

1 2 53
D != 0; 2 корня:
x1 = -1 + i*2┐/13
x2 = -1 - i*2┐/13

Петя заканчивает школу с пятёркой в аттестате, поступает в престижный университет и вылетает после первой же сессии.
Конец.
Итоговый код.

Автор: AraneusAdoro

Источник

Поделиться