二分法求函数的零点

GitHub跳转原题关系图返回列表

五次多项式无求根公式,用题面给出的异号端点与区间内唯一根作前提,实数二分 40 轮后输出中点。

OJ: roj

题目 ID: 1241

难度:普及-

标签:数学二分浮点数python

创建: 2026-09-30 01:19

更新: 2026-10-05 06:30

题目描述

有函数 f(x)=x5−15x4+85x3−225x2+274x−121f(x)=x^5-15x^4+85x^3-225x^2+274x-121。已知 f(1.5)>0f(1.5)>0、f(2.4)<0f(2.4)<0,且方程 f(x)=0f(x)=0 在区间 [1.5,2.4][1.5,2.4] 内有且只有一个根,请用二分法求出该根。

输入:无。输出:该根,四舍五入到小数点后 66 位(精确值为 1.8490158267…1.8490158267\ldots)。

思路

五次方程没有求根公式,但题面给出的"端点异号 + 区间内唯一根"正是二分法的全部前提:全程保持 f(left)>0>f(right)f(left)>0>f(right),每轮取中点只看 f(mid)f(mid) 的符号决定丢弃哪一半区间,根永远不会丢。停止条件直接写在区间宽度上(right-left > 1e-12),每轮减半,40 轮后区间宽约 8×10−138\times10^{-13},取中点输出即可保证 66 位小数正确;求值用秦九韶形式减少浮点误差。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 01:19
# update_at: 2026-09-30 01:27

LEFT = 1.5    # 题面保证 f(LEFT) > 0
RIGHT = 2.4   # 题面保证 f(RIGHT) < 0
EPS = 1e-12   # 区间宽度缩到 1e-12,比要求的 1e-6 精度小 6 个数量级


def f(x: float) -> float:
    """题面函数 f(x) 在 x 处的取值,用秦九韶(Horner)形式求值。"""
    return ((((x - 15) * x + 85) * x - 225) * x + 274) * x - 121


def solve() -> None:
    """在唯一零点已知所在区间上二分,输出四舍五入到 6 位小数的根。"""
    left, right = LEFT, RIGHT  # 不变量:f(left) > 0 且 f(right) < 0,零点始终被区间夹住
    while right - left > EPS:
        mid = (left + right) / 2
        if f(mid) > 0:
            left = mid   # 中点为正当,零点落在右半段
        else:
            right = mid  # 中点为负,零点落在左半段
    print(f"{(left + right) / 2:.6f}")


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 06:30
 * update_at: 2026-10-05 06:30
 */
#include <cstdio>

// 用秦九韶(Horner)形式计算 f(x),只需常数次乘加,避免高次幂的浮点误差放大
double f(double x) {
    return ((((x - 15) * x + 85) * x - 225) * x + 274) * x - 121;
}

int main() {
    // 题面保证 f(1.5) > 0 > f(2.4),且区间内有唯一根,直接以此为初始区间
    double left = 1.5, right = 2.4;

    // 终止条件写在区间宽度上:每轮减半,40 轮后区间宽 < 1e-12,
    // 取中点误差 <= 5e-13,远小于 6 位小数要求的 5e-7
    const double EPS = 1e-12;
    while (right - left > EPS) {
        double mid = (left + right) / 2;
        if (f(mid) > 0)
            left = mid; // f(mid) > 0:中点在根左侧,保留右半区间
        else
            right = mid; // f(mid) <= 0:中点越过根,保留左半区间
    }

    printf("%.6f\n", (left + right) / 2);
    return 0;
}