0

I have implemented this solution for finding a root of a cubic function

f(x) = ax3 + bx2 + cx + d

given a, b, c, and d, ensuring it's being monotonic.

After submitting the solution to an online judge without being shown the test cases, I am being faced by a time limit error. a, b, c, and d guarantee that the function is monotonic and we know it is being continuous. The code first finds the interval [A, B] such that f(A) * f(B) < 0; then the code moves to implement the bisection search.

What I want to know is if there is some possibility to minimize the time complexity of my code so it passes the online judge. The input is a, b, c, d, and the output should be the root with an error 0.000001.

Code:

#include <iostream>
#include <algorithm>
//#include <cmath>
//#include <string>

using namespace std;

int f(double a, double b, double c, double d, double x) {
    return x*(x*(a*x + b) + c) + d;
}

int main() {

    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);

    double a, b, c, d, A, B, x = 1, res;
    cin >> a >> b >> c >> d; 

    //determinning the interval
    double f_x = f(a, b, c, d, x);
    if (a > 0) { // strictly increasing
        if (f_x > 0) { B = 0;
            while (f(a, b, c, d, x) >= 0) { x -= x; }
            A = x; }
        else { A = 0;
            while (f(a, b, c, d, x) <= 0) { x += x; }
            B = x; }
    }

    else { //strictly decreasing
        if (f_x > 0) { A = 0;
            while (f(a, b, c, d, x) >= 0) { x += x; }
            B = x; }
        else { B = 0;
            while (f(a, b, c, d, x) <= 0) { x -= x; }
            A = x; }    
    }
    // Bisection Search
    double l = A;
    while ((B - A) >= 0.000001)
    {
        // Find middle point 
        l = (A + B) / 2;

        // Check if middle point is root 
        if (f(a, b, c, d, l) == 0.0)
            break;

        // Decide the side to repeat the steps 
        else if (f(a, b, c, d, l)*f(a, b, c, d, A) < 0)
            B = l;
        else
            A = l;
    }
    res = l;
    cout.precision(6);
    cout << fixed << " " << res;

    return 0;
}
Evg
  • 25,259
  • 5
  • 41
  • 83
v_head
  • 759
  • 4
  • 13

1 Answers1

5

There is no need to determine the initial interval, just take [-DBL_MAX, +DBL_MAX]. The tolerance can be chosen to be 1 ULP.

The following code implements these ideas:

// This function will be available in C++20 as std::midpoint
double midpoint(double x, double y) {
    if (std::isnormal(x) && std::isnormal(y))
        return x / 2 + y / 2;
    else
        return (x + y) / 2;
}

int main() {
    ...
    const auto fn = [=](double x) { return x * (x * (x * a + b) + c) + d; };

    auto left  = -std::numeric_limits<double>::max();
    auto right =  std::numeric_limits<double>::max();

    while (true) {
        const auto mid = midpoint(left, right);
        if (mid <= left || mid >= right)
            break;

        if (std::signbit(fn(left)) == std::signbit(fn(mid)))
            left = mid;
        else
            right = mid;
    }

    const double answer = left;
    ...
}

Initially, fn(x) can overflow and return inf. No special handling of this case is needed.

Evg
  • 25,259
  • 5
  • 41
  • 83