我参加聚会的时间已经很晚了,但我希望能给出一个更好的答案;更简短,(假设我)基准是对的)也很多更快.long goodMask; // 0xC840C04048404040 computed below{
for (int i=0; i<64; ++i) goodMask |= Long.MIN_VALUE >>> (i*i);}public boolean isSquare(long x) {
// This tests if the 6 least significant bits are right.
// Moving the to be tested bit to the highest position saves us masking.
if (goodMask <= 0) return false;
final int numberOfTrailingZeros = Long.numberOfTrailingZeros(x);
// Each square ends with an even number of zeros.
if ((numberOfTrailingZeros & 1) != 0) return false;
x >>= numberOfTrailingZeros;
// Now x is either 0 or odd.
// In binary each odd square ends with 001.
// Postpone the sign test until now; handle zero in the branch.
if ((x&7) != 1 | x <= 0) return x == 0;
// Do it in the classical way.
// The correctness is not trivial as the conversion from long to double is lossy!
final long tst = (long) Math.sqrt(x);
return tst * tst == x;}
第一次测试很快就能捕捉到大多数非正方形。它使用了一个64项表,它封装在一个长,所以没有数组访问成本(间接和边界检查)。对于一致随机long,有81.25%的可能性在这里结束。
第二个测试捕捉所有在其因式分解中有奇数的数字。方法Long.numberOfTrailingZeros是非常快的,因为它得到的JIT-编辑成一个单一的i86指令。
在删除尾随零后,第三次测试处理以二进制形式以011、101或111结尾的数字,这些数字不是完美的平方。它还关心负数,也处理0。
最后的测试又回到了double算术。如double只有53位尾数,从long到double包括大值的四舍五入。不过,测试是正确的(除非证明是错误的)。
试图整合mod255的想法是不成功的。