C++ 超出三次函数根对分搜索时间限制

C++ 超出三次函数根对分搜索时间限制,c++,numeric,bisection,C++,Numeric,Bisection,我已经实现了这个解决方案来寻找一个三次函数的根 f(x)=ax3+bx2+cx+d 给定a、b、c和d,确保它是单调的 在将解决方案提交给在线法官而没有向其展示测试用例之后,我面临着一个时间限制错误a、b、c和d保证函数是单调的,我们知道它是连续的。代码首先查找间隔[A,B],使得f(A)*f(B)>a>>b>>c>>d; //确定时间间隔 双f_x=f(a,b,c,d,x); 如果(a>0){//严格递增 如果(f_x>0){B=0; 而(f(a,b,c,d,x)>=0{x-=x;} A=x;

我已经实现了这个解决方案来寻找一个三次函数的根

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

给定
a
b
c
d
,确保它是单调的

在将解决方案提交给在线法官而没有向其展示测试用例之后,我面临着一个时间限制错误
a
b
c
d
保证函数是单调的,我们知道它是连续的。代码首先查找间隔
[A,B]
,使得
f(A)*f(B)<0
;然后,代码移动以实现对分搜索

我想知道的是,是否有可能将我的代码的时间复杂度降至最低,从而使其通过在线判断。输入为
a
b
c
d
,输出应为根,并带有错误
0.000001

代码:

#包括
#包括
//#包括
//#包括
使用名称空间std;
INTF(双a,双b,双c,双d,双x){
返回x*(x*(a*x+b)+c)+d;
}
int main(){
freopen(“input.txt”,“r”,stdin);
freopen(“output.txt”,“w”,stdout);
双a,b,c,d,a,b,x=1,res;
cin>>a>>b>>c>>d;
//确定时间间隔
双f_x=f(a,b,c,d,x);
如果(a>0){//严格递增
如果(f_x>0){B=0;
而(f(a,b,c,d,x)>=0{x-=x;}
A=x;}
否则{A=0;
而(f(a,b,c,d,x)0{a=0;
而(f(a,b,c,d,x)>=0{x+=x;}
B=x;}
else{B=0;
而(f(a,b,c,d,x)=0.000001)
{
//找到中间点
l=(A+B)/2;
//检查中点是否为根
如果(f(a,b,c,d,l)=0.0)
打破
//决定要重复这些步骤的一侧
否则如果(f(a,b,c,d,l)*f(a,b,c,d,a)<0)
B=l;
其他的
A=l;
}
res=l;
计算精度(6);

cout无需确定初始间隔,只需取
[-DBL\u MAX,+DBL\u MAX]
。公差可选择为1

以下代码实现了这些想法:

// 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;
    ...
}
//此函数将在C++20中作为std::middpoint提供
双中点(双x,双y){
如果(标准::isnormal(x)和&std::isnormal(y))
返回x/2+y/2;
其他的
返回(x+y)/2;
}
int main(){
...
const auto fn=[=](双x){返回x*(x*(x*a+b)+c)+d;};
自动左=-std::numeric_limits::max();
auto right=std::numeric_limits::max();
while(true){
常数自动中点=中点(左、右);
如果(中间=右侧)
打破
如果(std::signbit(fn(左))==std::signbit(fn(中)))
左=中;
其他的
右=中;
}
常数双答案=左;
...
}

最初,
fn(x)
可能溢出并返回
inf
。不需要对这种情况进行特殊处理。

如果(f(a,b,c,d,l)==0.0)
--检查精确的0.0不是一个好主意。我今天早些时候已经看到了代码。
x-=x;
仍然是错误的;)
x-=x;
x=0;
相同,如果这不能让你离开while循环,你就永远不会离开它。
x-=x;
x=x-x;
完全相同,也就是…
x=0;
@Ripi2不,我不是!我只是说,
x=0;
不可能被纠正很难被击败,任何更好的开始间隔只会节省几次迭代非常令人印象深刻。速度提升的一点空间:这里的函数每次迭代调用两次。如果需要,可以轻松处理
// 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;
    ...
}