确定整数的平方根是否为整数的最快方法

≡放荡痞女 提交于 2020-08-15 06:32:23

问题:

I'm looking for the fastest way to determine if a long value is a perfect square (ie its square root is another integer): 我正在寻找确定long值是否是完美平方(即,其平方根是另一个整数)的最快方法:

  1. I've done it the easy way, by using the built-in Math.sqrt() function, but I'm wondering if there is a way to do it faster by restricting yourself to integer-only domain. 通过使用内置的Math.sqrt()函数,我已经完成了简单的方法,但是我想知道是否有一种方法可以通过将自己限制在仅整数域中来更快地完成操作。
  2. Maintaining a lookup table is impractical (since there are about 2 31.5 integers whose square is less than 2 63 ). 维护查找表是不切实际的(因为大约有2 31.5个整数,其平方小于2 63 )。

Here is the very simple and straightforward way I'm doing it now: 这是我现在做的非常简单明了的方法:

public final static boolean isPerfectSquare(long n)
{
  if (n < 0)
    return false;

  long tst = (long)(Math.sqrt(n) + 0.5);
  return tst*tst == n;
}

Note: I'm using this function in many Project Euler problems. 注意:我在许多Project Euler问题中都使用了此功能。 So no one else will ever have to maintain this code. 因此,没有其他人将不得不维护此代码。 And this kind of micro-optimization could actually make a difference, since part of the challenge is to do every algorithm in less than a minute, and this function will need to be called millions of times in some problems. 这种微优化实际上可能会有所作为,因为挑战的一部分是在不到一分钟的时间内完成每种算法,并且在某些问题中,需要数百万次调用此函数。


I've tried the different solutions to the problem: 我已经尝试过不同的解决方案:

  • After exhaustive testing, I found that adding 0.5 to the result of Math.sqrt() is not necessary, at least not on my machine. 经过详尽的测试后,我发现没有必要在Math.sqrt()的结果中加上0.5 ,至少在我的机器上没有。
  • The fast inverse square root was faster, but it gave incorrect results for n >= 410881. However, as suggested by BobbyShaftoe , we can use the FISR hack for n < 410881. 快速反平方根更快,但是对于n> = 410881,它给出的结果不正确。但是,如BobbyShaftoe所建议,我们可以对n <410881使用FISR hack。
  • Newton's method was a good bit slower than Math.sqrt() . 牛顿的方法比Math.sqrt()慢很多。 This is probably because Math.sqrt() uses something similar to Newton's Method, but implemented in the hardware so it's much faster than in Java. 这可能是因为Math.sqrt()使用类似于牛顿方法的方法,但是在硬件中实现,因此它比Java快得多。 Also, Newton's Method still required use of doubles. 同样,牛顿法仍然需要使用双精度。
  • A modified Newton's method, which used a few tricks so that only integer math was involved, required some hacks to avoid overflow (I want this function to work with all positive 64-bit signed integers), and it was still slower than Math.sqrt() . 一种经过改进的牛顿方法,其中使用了一些技巧,以便仅涉及整数数学,因此需要一些技巧来避免溢出(我希望此函数与所有正的64位带符号整数一起使用),但它仍然比Math.sqrt()Math.sqrt()
  • Binary chop was even slower. 二进制印章甚至更慢。 This makes sense because the binary chop will on average require 16 passes to find the square root of a 64-bit number. 这是有道理的,因为二进制印章平均需要16次通过才能找到64位数字的平方根。
  • According to John's tests, using or statements is faster in C++ than using a switch , but in Java and C# there appears to be no difference between or and switch . 根据John的测试,在C ++中使用or语句比使用switch更快,但是在Java和C#中, orswitch之间似乎没有区别。
  • I also tried making a lookup table (as a private static array of 64 boolean values). 我还尝试制作一个查找表(作为64个布尔值的私有静态数组)。 Then instead of either switch or or statement, I would just say if(lookup[(int)(n&0x3F)]) { test } else return false; 然后,我会说if(lookup[(int)(n&0x3F)]) { test } else return false;而不是switch或or语句, if(lookup[(int)(n&0x3F)]) { test } else return false; . To my surprise, this was (just slightly) slower. 令我惊讶的是,这慢了一点。 This is because array bounds are checked in Java . 这是因为数组边界是在Java中检查的

解决方案:

参考一: https://stackoom.com/question/1EtP/确定整数的平方根是否为整数的最快方法
参考二: https://oldbug.net/q/1EtP/Fastest-way-to-determine-if-an-integer-s-square-root-is-an-integer
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!