【问题标题】:Function to check for perfect square not working for large number检查完美正方形的功能不适用于大量数字
【发布时间】:2022-02-04 18:28:13
【问题描述】:

我遇到了一种算法,可以在 O(logN) 时间内判断给定数字是否是完美平方。

这是这个想法的实现(JAVA)。

public boolean isPerfectSquare(long x) {
        if (x <= 1)
            return true;
        
        long low = 1;
        long high = x;
        long mid = 0;
        while (low <= high) {
            mid = low + (high - low) / 2l;
            if (mid * mid == x)
                return true;
            else if (mid * mid < x)
                low = mid + 1;
            else
                high = mid - 1;
        }
        
        return false;
    }

这适用于256808201 等数字 但对于像999966000289 这样的数字则失败。

我不知道为什么?

【问题讨论】:

  • 可能是因为 mid*mid 溢出了。
  • 因为 C 中的数值类型有其局限性。你应该问自己,当你喂它999966000289 时,mid * mid 是什么,它是否适合你正在使用的long 类型。还有可能你的系统中的long 甚至不能容纳999966000289 本身。
  • 现在说得通了。有没有办法检查大数的平方根属性?
  • @Insane_banda 考虑将mid 设为long long int。当您使用它时,您还可以对lowhighmid 使用无符号类型,以获得另一个因子 2。
  • @pmg:当你接近根时,牛顿的方法是有效的。对于初始化,最好找到最接近的 4 的幂(很容易通过移位完成)并计算 2 的相同幂。

标签: java algorithm data-structures perfect-square


【解决方案1】:

正如 cmets 中提到的,问题是中间 mid*mid 可能会溢出。使用无符号类型和“long”或“long long”变体会有所帮助。

但是,对于lowhigh 的初始值,mid 的第一个值接近x/4。如果x 很大,这就是平方根的很大过冲。

因此,我们可以通过改进初始 lowhigh 限制估计来改进可管理数字的范围。

免责声明:堆栈溢出格式不适合长时间分析。我有一个很好的论据,以下是有效的,我在下面包含了其中的一部分,但是完整的分析太长了,无法在此处包含。

bool isPerfectSquare(unsigned long x) {
    if (x <= 1)
        return true;
        
    unsigned long low = 1;
    unsigned long high = x;

    // Improve the low/high limits
    while((low<<1) < (high>>1))
    {
        low <<= 1;
        high >>= 1;
    }

    unsigned long mid = 0;
    while (low <= high) {
        mid = low + (high - low) / 2l;
        if (mid * mid == x)
            return true;
        else if (mid * mid < x)
            low = mid + 1;
        else
            high = mid - 1;
    }
    return false;
}

通过此修改,mid 的初始值对于较大的 x 值要小得多,因此可以处理较大的 x 值而不会溢出。

证明下限不会超过平方根并不难,这样做说明了这种方法背后的直觉:

对于一些t,其中1&lt;=t&lt;2x=t*2^r对于一些整数,r。因此:

    sqrt(x) = sqrt(t) * 2^(r/2)

这意味着

    2^(r/2) <= sqrt(x) < 2^(r/2+1)

因此,下限是二进制1 移动到一半(当r 为偶数时)或尽可能接近(当r 为奇数时)到二进制表示中最左边的1 位x。这正是while-loop 中发生的事情。

证明high确实是while循环后平方根的上界需要更长的分析时间。

【讨论】:

  • "...但是完整的分析太长了,无法在此处包含。"你需要更大的保证金。 :)
  • @rossum 是的,我希望这篇文章能在 300 年内成名 :-)
猜你喜欢
  • 2022-08-23
  • 2015-11-06
  • 2021-06-24
  • 2019-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多