【发布时间】:2010-09-22 16:11:01
【问题描述】:
我正在寻找确定long 值是否是完美平方(即其平方根是另一个整数)的最快方法:
- 我使用内置的
Math.sqrt()以简单的方式做到了这一点 功能,但我想知道是否有办法更快地做到这一点 将自己限制在仅整数域。 - 维护查找表是不切实际的(因为大约有 231.5 平方小于 263 的整数。
这是我现在正在做的非常简单直接的方法:
public final static boolean isPerfectSquare(long n)
{
if (n < 0)
return false;
long tst = (long)(Math.sqrt(n) + 0.5);
return tst*tst == n;
}
注意:我在很多Project Euler 问题中都使用了这个函数。因此,没有其他人将不得不维护此代码。而这种微优化实际上可以产生影响,因为部分挑战是在不到一分钟的时间内完成每个算法,并且在某些问题中需要调用数百万次。
我尝试了不同的解决方案:
- 经过详尽的测试,我发现在 Math.sqrt() 的结果中添加
0.5是没有必要的,至少在我的机器上没有。 - fast inverse square root 更快,但它在 n >= 410881 时给出了不正确的结果。但是,正如 BobbyShaftoe 所建议的,我们可以对 n
- Newton 的方法比
Math.sqrt()慢了很多。这可能是因为Math.sqrt()使用类似于牛顿法的东西,但在硬件中实现,因此它比在 Java 中快得多。此外,牛顿法仍然需要使用双精度数。 - 修改后的牛顿方法,它使用了一些技巧,因此只涉及整数数学,需要一些技巧来避免溢出(我希望这个函数可以处理所有正的 64 位有符号整数),它仍然比
Math.sqrt()。 - 二进制斩波甚至更慢。这是有道理的,因为二进制斩波平均需要 16 次通过才能找到 64 位数字的平方根。
- 根据 John 的测试,在 C++ 中使用
or语句比使用switch更快,但在 Java 和 C# 中,or和switch之间似乎没有区别。 - 我还尝试制作一个查找表(作为 64 个布尔值的私有静态数组)。然后不是 switch 或
or语句,我只想说if(lookup[(int)(n&0x3F)]) { test } else return false;。令我惊讶的是,这(只是稍微)慢了一点。这是因为array bounds are checked in Java。
【问题讨论】:
-
这是 Java 代码,其中 int==32 位和 long==64 位,并且都是有符号的。
-
@Shreevasta:我已经对大值(大于 2^53)进行了一些测试,您的方法给出了一些误报。遇到的第一个是 n=9007199326062755,它不是一个完美的正方形,而是作为一个返回。
-
请不要将其称为“John Carmack hack”。不是他想出来的。
-
@mamama -- 或许,但这要归功于他。亨利福特没有发明汽车,莱特兄弟没有发明飞机,加莱奥也不是第一个发现地球绕太阳转的人……世界是由偷来的发明组成的(而且爱)。
-
您可能会通过使用
((1<<(n&15))|65004) != 0之类的方法来略微提高“快速失败”的速度,而不是进行三个单独的检查。
标签: java math optimization perfect-square