题目描述

:::fold-green 相关原题

✅ 求解立方根

牛客限定输入绝对值不超过 20,输出保留一位小数;本文函数返回未格式化的近似值。

:::

给定有限实数 x,求它的实数立方根的近似值并返回,即求满足 r × r × r = x 的实数 r。

输入可以为正数、负数或零;负数的实数立方根仍是负数。

示例 1:

输入:x = 8.0
输出:2.0

示例 2:

输入:x = -0.125
输出:-0.5

提示:

  • x 是有限实数。
  • 返回浮点近似值;本题未指定统一的绝对误差或相对误差阈值。
  • 示例列出的是对应输入的数学结果。

题意分析

给定实数 x,求它的实数立方根近似值,也就是使 r³ 接近 x 的数 r。立方函数严格递增,所以实数立方根唯一;负数也有实数立方根,其符号与输入相同。

CodeTop 只提供了题名,未公布输入范围或误差指标。下方保留固定八十轮浮点二分实现,用来说明区间如何收缩;它的精度取决于输入量级和浮点运算,不能据此承诺对任意量级输入都满足某个统一相对误差。

解法:浮点二分

核心思路

[!blue]

立方函数满足 (-r)³ = -r³,因此先记录输入是否为负,只对 target = |x| 求非负根,最后恢复符号。这样所有二分比较都可以在非负区间内完成。

先建立包含真根的区间 [0, max(1, target)]。当 target >= 1 时,target³ >= target,所以上界取 target 足够;当 0 <= target < 1 时,立方根可能比 target 大,上界至少取一才能覆盖答案。左端零的立方不大于目标。

每轮取中点 mid。如果 mid³ < target,由于立方函数递增,根在中点右侧,令 left = mid;否则令 right = mid。两种更新都在理想实数运算下保留根,并将区间长度减半。这里求的是实数近似值,更新时不能像整数二分那样加一或减一。

代码固定执行八十轮,再返回区间中点。若忽略浮点舍入,初始区间宽度为 R,最终宽度为 R / 2^80,中点与真根的距离不超过 R / 2^81。这个结论说明的是绝对误差与初始范围的关系,而不是任意输入都能保证同样的相对精度。

实际计算还受浮点表示和立方乘法的舍入、溢出或下溢影响,不能把理想误差公式当作所有浮点输入的无条件保证。固定轮数能确保循环结束,也避免因等待浮点数恰好相等而无法停止。输入零时,当前代码会得到一个接近零的中点,而不是通过特判直接返回精确零。

解题步骤

  1. 保存输入符号,令目标为输入的绝对值。
  2. 初始化 left = 0、right = max(1, target)。
  3. 重复八十轮:计算中点并比较中点的立方与目标,小于目标则更新左端,否则更新右端。
  4. 取最终区间中点作为根的大小,按原输入符号返回结果。

代码实现

class Solution {
    public double cubeRoot(double x) {
        // 先分离符号,只在非负区间比较立方
        boolean negative = x < 0;
        double target = Math.abs(x);
        double left = 0.0;
        // 小于一时真根可能大于输入,上界至少取一
        double right = Math.max(1.0, target);

        // 固定轮数得到近似结果,精度仍取决于初始宽度和浮点舍入
        for (int i = 0; i < 80; i++) {
            double mid = left + (right - left) / 2.0;

            if (mid * mid * mid < target) {
                left = mid;
            } else {
                right = mid;
            }
        }

        double root = left + (right - left) / 2.0;

        return negative ? -root : root;
    }
}
func cubeRoot(x float64) float64 {
    // 先分离符号,只在非负区间比较立方
    negative := x < 0
    target := x
    if target < 0 {
        target = -target
    }

    left, right := 0.0, target
    // 小于一时真根可能大于输入,上界至少取一
    if right < 1 {
        right = 1
    }

    // 固定轮数得到近似结果,精度仍取决于初始宽度和浮点舍入
    for i := 0; i < 80; i++ {
        mid := left + (right-left)/2
        if mid*mid*mid < target {
            left = mid
        } else {
            right = mid
        }
    }

    root := left + (right-left)/2
    if negative {
        return -root
    }
    return root
}

复杂度分析

  • 时间复杂度:当前实现固定八十轮,为 $O(1)$。若在理想实数运算下按目标绝对误差 $\varepsilon$ 选择轮数,初始宽度为 R,需要 $O(1 + \max(0, \log(R/\varepsilon)))$ 轮。
  • 空间复杂度:$O(1)$,只维护符号、目标值和少量浮点边界变量。

关键点总结

[!green]

  • 先找到包含真根的区间,再利用立方函数的严格单调性决定保留哪一半。
  • 绝对值小于一时,不能直接把输入绝对值当作上界,必须覆盖更大的根。
  • 八十轮定义了本实现的停止方式;实际精度仍应结合输入范围、误差要求和浮点表示判断。

易错点总结

[!yellow]

  • 上界只取 |x|,会排除绝对值介于零和一之间输入的真根。
  • 边界更新写成 mid + 1 或 mid - 1,会跳过可能包含实数答案的区间。
  • 对负数直接套只覆盖非负根的区间,或求完绝对值根后忘记恢复符号,会得到错误方向的结果。
  • 用 mid³ == target 作为唯一退出条件,不能保证浮点运算一定恰好命中。
  • 把固定轮数、理想算术的绝对误差和实际浮点相对误差混为一谈,会夸大这段实现能够保证的精度范围。

相似题目

题目 难度 关联与区别
69. x 的平方根 简单 都对单调幂函数二分,本题求实数近似且支持负数,整数平方根题返回下取整值。
补充题 149. 实数平方根的近似计算 中等 都需明确浮点误差或迭代终止条件,本题把平方关系改成立方,负值通过符号恢复处理。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2025/37081036
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!