我正在寻找确定long值是否是完美平方(即,其平方根是另一个整数)的最快方法:

  1. 通过使用内置的Math.sqrt()函数,我已经完成了简单的方法,但是我想知道是否有一种方法可以通过将自己限制在仅整数域中来更快地完成操作。
  2. 维护查找表是不切实际的(因为大约有2 31.5个整数,其平方小于2 63 )。

这是我现在做的非常简单明了的方法:

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 ,至少在我的机器上没有。
  • 快速反平方根更快,但是对于n> = 410881,它给出的结果不正确。但是,如BobbyShaftoe所建议,我们可以对n <410881使用FISR hack。
  • 牛顿的方法比Math.sqrt()慢很多。 这可能是因为Math.sqrt()使用类似于牛顿方法的方法,但是在硬件中实现,因此它比Java快得多。 同样,牛顿法仍然需要使用双精度。
  • 一种经过改进的牛顿方法,其中使用了一些技巧,以便仅涉及整数数学,因此需要一些技巧来避免溢出(我希望此函数与所有正的64位带符号整数一起使用),但它仍然比Math.sqrt()Math.sqrt()
  • 二进制印章甚至更慢。 这是有道理的,因为二进制印章平均需要16次通过才能找到64位数字的平方根。
  • 根据John的测试,在C ++中使用or语句比使用switch更快,但是在Java和C#中, orswitch之间似乎没有区别。
  • 我还尝试制作一个查找表(作为64个布尔值的私有静态数组)。 然后,我会说if(lookup[(int)(n&0x3F)]) { test } else return false;而不是switch或or语句, if(lookup[(int)(n&0x3F)]) { test } else return false; 。 令我惊讶的是,这慢了一点。 这是因为数组边界是在Java中检查的 。

#1楼

“我正在寻找确定长值是否是完美平方(即,其平方根是另一个整数)的最快方法。”

答案令人印象深刻,但我看不到一个简单的检查:

检查长整数右边的第一个数字是否是集合(0,1,4,5,6,9)的成员。 如果不是,则不可能是“完美的正方形”。

例如。

4567-不可能是完美的正方形。


#2楼

我对该线程中的几种算法进行了自己的分析,并得出了一些新结果。 您可以在此答案的编辑历史记录中看到那些较旧的结果,但是由于我犯了一个错误,它们并不准确,并且浪费了时间来分析几种不太接近的算法。 但是,从几个不同的答案中吸取教训,我现在有两种算法可以压碎该线程的“赢家”。 这是我与其他人不同的核心工作:

// This is faster because a number is divisible by 2^4 or more only 6% of the time
// and more than that a vanishingly small percentage.
while((x & 0x3) == 0) x >>= 2;
// This is effectively the same as the switch-case statement used in the original
// answer.
if((x & 0x7) != 1) return false;

但是,这种简单的行(通常在大多数情况下添加一条或两条非常快的指令)大大简化了将switch-case语句简化为一个if语句的过程。 但是,如果许多测试数字具有显着的2的幂,它可能会增加运行时间。

以下算法如下:

  • 互联网 -Kip发布的答案
  • 杜伦(Durron) -我的修改后的答案以单次通过答案为基础
  • DurronTwo-我的修改后的答案使用了两次通过答案(@JohnnyHeggheim),并进行了其他一些小的修改。

如果数字是使用Math.abs(java.util.Random.nextLong())生成的,这是一个示例运行时

 0% Scenario{vm=java, trial=0, benchmark=Internet} 39673.40 ns; ?=378.78 ns @ 3 trials
33% Scenario{vm=java, trial=0, benchmark=Durron} 37785.75 ns; ?=478.86 ns @ 10 trials
67% Scenario{vm=java, trial=0, benchmark=DurronTwo} 35978.10 ns; ?=734.10 ns @ 10 trialsbenchmark   us linear runtimeInternet 39.7 ==============================Durron 37.8 ============================
DurronTwo 36.0 ===========================vm: java
trial: 0

如果仅在前一百万个long上运行,则这是一个示例运行时:

 0% Scenario{vm=java, trial=0, benchmark=Internet} 2933380.84 ns; ?=56939.84 ns @ 10 trials
33% Scenario{vm=java, trial=0, benchmark=Durron} 2243266.81 ns; ?=50537.62 ns @ 10 trials
67% Scenario{vm=java, trial=0, benchmark=DurronTwo} 3159227.68 ns; ?=10766.22 ns @ 3 trialsbenchmark   ms linear runtimeInternet 2.93 ===========================Durron 2.24 =====================
DurronTwo 3.16 ==============================vm: java
trial: 0

如您所见, DurronTwo在大型输入上表现更好,因为它非常频繁地使用魔术,但是与第一种算法和Math.sqrt相比,它变得DurronTwo ,因为数字小得多。 同时,较简单的Durron是一个巨大的赢家,因为在前100万个数字中,它Durron将其除以4。

这是Durron

public final static boolean isPerfectSquareDurron(long n) {if(n < 0) return false;if(n == 0) return true;long x = n;// This is faster because a number is divisible by 16 only 6% of the time// and more than that a vanishingly small percentage.while((x & 0x3) == 0) x >>= 2;// This is effectively the same as the switch-case statement used in the original// answer. if((x & 0x7) == 1) {long sqrt;if(x < 410881L){int i;float x2, y;x2 = x * 0.5F;y  = x;i  = Float.floatToRawIntBits(y);i  = 0x5f3759df - ( i >> 1 );y  = Float.intBitsToFloat(i);y  = y * ( 1.5F - ( x2 * y * y ) );sqrt = (long)(1.0F/y);} else {sqrt = (long) Math.sqrt(x);}return sqrt*sqrt == x;}return false;
}

DurronTwo

public final static boolean isPerfectSquareDurronTwo(long n) {if(n < 0) return false;// Needed to prevent infinite loopif(n == 0) return true;long x = n;while((x & 0x3) == 0) x >>= 2;if((x & 0x7) == 1) {long sqrt;if (x < 41529141369L) {int i;float x2, y;x2 = x * 0.5F;y = x;i = Float.floatToRawIntBits(y);//using the magic number from //http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf//since it more accuratei = 0x5f375a86 - (i >> 1);y = Float.intBitsToFloat(i);y = y * (1.5F - (x2 * y * y));y = y * (1.5F - (x2 * y * y)); //Newton iteration, more accuratesqrt = (long) ((1.0F/y) + 0.2);} else {//Carmack hack gives incorrect answer for n >= 41529141369.sqrt = (long) Math.sqrt(x);}return sqrt*sqrt == x;}return false;
}

我的基准测试工具:(需要Google caliper 0.1-rc5)

public class SquareRootBenchmark {public static class Benchmark1 extends SimpleBenchmark {private static final int ARRAY_SIZE = 10000;long[] trials = new long[ARRAY_SIZE];@Overrideprotected void setUp() throws Exception {Random r = new Random();for (int i = 0; i < ARRAY_SIZE; i++) {trials[i] = Math.abs(r.nextLong());}}public int timeInternet(int reps) {int trues = 0;for(int i = 0; i < reps; i++) {for(int j = 0; j < ARRAY_SIZE; j++) {if(SquareRootAlgs.isPerfectSquareInternet(trials[j])) trues++;}}return trues;   }public int timeDurron(int reps) {int trues = 0;for(int i = 0; i < reps; i++) {for(int j = 0; j < ARRAY_SIZE; j++) {if(SquareRootAlgs.isPerfectSquareDurron(trials[j])) trues++;}}return trues;   }public int timeDurronTwo(int reps) {int trues = 0;for(int i = 0; i < reps; i++) {for(int j = 0; j < ARRAY_SIZE; j++) {if(SquareRootAlgs.isPerfectSquareDurronTwo(trials[j])) trues++;}}return trues;   }}public static void main(String... args) {Runner.main(Benchmark1.class, args);}
}

更新:我做了一个新的算法,在某些情况下更快,而在另一些情况下则很慢,我根据不同的输入获得了不同的基准。 如果我们计算模0xFFFFFF = 3 x 3 x 5 x 7 x 13 x 17 x 241 ,则可以消除97.82%不能为平方的数字。 可以用5个按位运算在一行中完成(某种程度上):

if (!goodLookupSquares[(int) ((n & 0xFFFFFFl) + ((n >> 24) & 0xFFFFFFl) + (n >> 48))]) return false;

所得索引为1)残基,2)残基+ 0xFFFFFF ,或3)残基+ 0x1FFFFFE 。 当然,我们需要有一个残差查找表,其模为0xFFFFFF ,大约为3mb文件(在这种情况下,存储为ascii文本十进制数字,不是最佳值,但显然可以通过ByteBuffer改进,依此类推。)但这是预先计算的结果没关系, 您可以在这里找到文件 (或自己生成文件 ):

public final static boolean isPerfectSquareDurronThree(long n) {if(n < 0) return false;if(n == 0) return true;long x = n;while((x & 0x3) == 0) x >>= 2;if((x & 0x7) == 1) {if (!goodLookupSquares[(int) ((n & 0xFFFFFFl) + ((n >> 24) & 0xFFFFFFl) + (n >> 48))]) return false;long sqrt;if(x < 410881L){int i;float x2, y;x2 = x * 0.5F;y  = x;i  = Float.floatToRawIntBits(y);i  = 0x5f3759df - ( i >> 1 );y  = Float.intBitsToFloat(i);y  = y * ( 1.5F - ( x2 * y * y ) );sqrt = (long)(1.0F/y);} else {sqrt = (long) Math.sqrt(x);}return sqrt*sqrt == x;}return false;
}

我将其加载到boolean数组中,如下所示:

private static boolean[] goodLookupSquares = null;public static void initGoodLookupSquares() throws Exception {Scanner s = new Scanner(new File("24residues_squares.txt"));goodLookupSquares = new boolean[0x1FFFFFE];while(s.hasNextLine()) {int residue = Integer.valueOf(s.nextLine());goodLookupSquares[residue] = true;goodLookupSquares[residue + 0xFFFFFF] = true;goodLookupSquares[residue + 0x1FFFFFE] = true;}s.close();
}

示例运行时。 在我进行的每次试用中,它都击败了Durron (版本1)。

 0% Scenario{vm=java, trial=0, benchmark=Internet} 40665.77 ns; ?=566.71 ns @ 10 trials
33% Scenario{vm=java, trial=0, benchmark=Durron} 38397.60 ns; ?=784.30 ns @ 10 trials
67% Scenario{vm=java, trial=0, benchmark=DurronThree} 36171.46 ns; ?=693.02 ns @ 10 trialsbenchmark   us linear runtimeInternet 40.7 ==============================Durron 38.4 ============================
DurronThree 36.2 ==========================vm: java
trial: 0

#3楼

我参加聚会很晚了,但是我希望提供一个更好的答案。 更短(假设我的基准是正确的)也快得多 。

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 << x >= 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位尾数,因此从longdouble的转换包括对大值的舍入。 但是,测试是正确的(除非证明是错误的)。

尝试纳入mod255的想法没有成功。


#4楼

整数问题值得一个整数解决方案。 从而

对(非负)整数进行二进制搜索以找到最大整数t,使得t**2 <= n 。 然后测试r**2 = n是否准确。 这需要时间O(log n)。

如果您不知道如何对正整数进行二进制搜索(因为集合是无界的),那么这很容易。 首先,以f(t) = t**2 - n幂计算递增函数f(在f(t) = t**2 - n之上)。 当您看到它变成正数时,您已经找到一个上限。 然后,您可以进行标准的二进制搜索。


#5楼

maaartinus解决方案的以下简化似乎将运行时缩短了几个百分点,但是我不足以进行基准测试以产生可以信赖的基准测试:

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 << x >= 0) return false;// Remove an even number of trailing zeros, leaving at most one.x >>= (Long.numberOfTrailingZeros(x) & (-2);// Repeat the test on the 6 least significant remaining bits.if (goodMask << x >= 0 | 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;
}

值得一试的是如何省略第一次测试,

if (goodMask << x >= 0) return false;

会影响性能。


#6楼

通过结合使用该线程中其他人员提出的技术,这是我能想到的最快的Java实现。

  • Mod-256测试
  • 不精确的mod-3465测试(避免整数除法,但会带来一些误报)
  • 浮点平方根,取整并与输入值进行比较

我还尝试了这些修改,但是它们对性能没有帮助:

  • 附加的mod-255测试
  • 将输入值除以4的幂
  • 快速逆平方根(要获得较高的N值,它需要进行3次迭代,足以使其比硬件平方根函数慢。)
public class SquareTester {public static boolean isPerfectSquare(long n) {if (n < 0) {return false;} else {switch ((byte) n) {case -128: case -127: case -124: case -119: case -112:case -111: case -103: case  -95: case  -92: case  -87:case  -79: case  -71: case  -64: case  -63: case  -60:case  -55: case  -47: case  -39: case  -31: case  -28:case  -23: case  -15: case   -7: case    0: case    1:case    4: case    9: case   16: case   17: case   25:case   33: case   36: case   41: case   49: case   57:case   64: case   65: case   68: case   73: case   81:case   89: case   97: case  100: case  105: case  113:case  121:long i = (n * INV3465) >>> 52;if (! good3465[(int) i]) {return false;} else {long r = round(Math.sqrt(n));return r*r == n; }default:return false;}}}private static int round(double x) {return (int) Double.doubleToRawLongBits(x + (double) (1L << 52));}/** 3465<sup>-1</sup> modulo 2<sup>64</sup> */private static final long INV3465 = 0x8ffed161732e78b9L;private static final boolean[] good3465 =new boolean[0x1000];static {for (int r = 0; r < 3465; ++ r) {int i = (int) ((r * r * INV3465) >>> 52);good3465[i] = good3465[i+1] = true;}}}

#7楼

如果您想提高速度,考虑到您的整数是有限大小的,我怀疑最快的方法将涉及(a)按大小划分参数(例如,按最大位集划分类别),然后根据完美平方数组检查值在该范围内。


#8楼

我当时在想我在数值分析课程中度过的可怕时光。

然后我记得,雷神之锤的源代码在网上绕过了这个函数:

float Q_rsqrt( float number )
{long i;float x2, y;const float threehalfs = 1.5F;x2 = number * 0.5F;y  = number;i  = * ( long * ) &y;  // evil floating point bit level hackingi  = 0x5f3759df - ( i >> 1 ); // wtf?y  = * ( float * ) &i;y  = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration// y  = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed#ifndef Q3_VM#ifdef __linux__assert( !isnan(y) ); // bk010122 - FPE?#endif#endifreturn y;
}

它基本上使用牛顿的逼近函数来计算平方根(请记住确切的名称)。

它应该是可用的,甚至可能更快,它来自于惊人的id软件的游戏之一!

它是用C ++编写的,但是一旦有了这个主意,在Java中重用相同的技术就不难了:

我最初在以下位置找到它: http : //www.codemaestro.com/reviews/9

牛顿的方法在Wikipedia上进行了解释: http : //en.wikipedia.org/wiki/Newton%27s_method

您可以单击该链接以获取有关其工作原理的更多说明,但是如果您不太在意,那么从阅读博客和参加“数值分析”课程的过程中,我大致记得这一点:

  • * (long*) &y基本上是一个快速转换为long函数,因此可以对原始字节应用整数运算。
  • 0x5f3759df - (i >> 1); line是近似函数的预先计算的种子值。
  • * (float*) &i将值转换回浮点数。
  • y = y * ( threehalfs - ( x2 * y * y ) )行再次对该函数的值进行基本迭代。

逼近函数对结果的迭代次数越多,逼近函数给出的值越精确。 在Quake的情况下,一次迭代“足够好”,但是如果不适合您……那么您可以根据需要添加任意数量的迭代。

这应该更快一些,因为它将天真的平方根除以简单的除以2的除法操作数(实际上是* 0.5F乘法运算),并用一些固定数量的乘法运算代替。


#9楼

如果您进行二进制运算以尝试找到“正确的”平方根,则可以很容易地检测出所获得的值是否足够接近以告知:

(n+1)^2 = n^2 + 2n + 1
(n-1)^2 = n^2 - 2n + 1

因此,在计算了n^2 ,这些选项是:

  • n^2 = target :完成,返回true
  • n^2 + 2n + 1 > target > n^2 :距离很近,但并不完美:return false
  • n^2 - 2n + 1 < target < n^2 :同上
  • target < n^2 - 2n + 1 :在较低的n上进行二进制斩
  • target > n^2 + 2n + 1 :在更高的n上进行二进制斩

(对不起,这使用n作为您当前的猜测,并作为参数的target 。对于造成的困惑,表示歉意!)

我不知道这是否会更快,但是值得一试。

编辑:二进制印章并不一定要接受整数的整个范围,或者(2^x)^2 = 2^(2x) ,所以一旦您在目标中找到了最高设置位(可以是有点花哨的技巧;我完全忘记了如何),您可以快速获得一系列潜在的答案。 请注意,幼稚的二进制印章最多只需要进行31或32次迭代。


#10楼

我不确定是否会更快,甚至更准确,但是您可以使用John Carmack的Magical Square Root算法更快地求解平方根。 您可能可以轻松地针对所有可能的32位整数对此进行测试,并验证您是否确实获得了正确的结果,因为它只是一个近似值。 但是,现在考虑到这一点,使用双精度也是近似值,因此我不确定该如何发挥作用。


#11楼

您必须进行一些基准测试。 最佳算法将取决于输入的分布。

您的算法可能几乎是最佳的,但是您可能需要在调用平方根例程之前进行快速检查以排除某些可能性。 例如,通过按位进行“与”运算,以十六进制形式查看数字的最后一位。 完美平方只能以16为底的0、1、4或9结尾,因此对于75%的输入(假设它们是均匀分布的),您可以避免调用平方根来换取一些非常快速的比特摆动。

Kip对实现以下十六进制技巧的以下代码进行了基准测试。 测试数字1到100,000,000时,此代码的运行速度是原始代码的两倍。

public final static boolean isPerfectSquare(long n)
{if (n < 0)return false;switch((int)(n & 0xF)){case 0: case 1: case 4: case 9:long tst = (long)Math.sqrt(n);return tst*tst == n;default:return false;}
}

当我在C ++中测试类似代码时,它实际上比原始代码运行得慢。 但是,当我消除了switch语句时,十六进制把戏再次使代码快了两倍。

int isPerfectSquare(int n)
{int h = n & 0xF;  // h is the last hex "digit"if (h > 9)return 0;// Use lazy evaluation to jump out of the if statement as soon as possibleif (h != 2 && h != 3 && h != 5 && h != 6 && h != 7 && h != 8){int t = (int) floor( sqrt((double) n) + 0.5 );return t*t == n;}return 0;
}

消除switch语句对C#代码影响很小。


#12楼

使用牛顿方法计算整数平方根 ,然后对该数字求平方并进行校验,就像在当前解决方案中一样,应该更快。 牛顿法是其他答案中提到的Carmack解决方案的基础。 您应该能够得到更快的答案,因为您只对根的整数部分感兴趣,从而可以更快地停止近似算法。

您可以尝试的另一种优化方法:如果数字的数字根 以1、4、7或9结尾,则该数字不是完美的平方。 这可以用作在应用较慢的平方根算法之前消除60%输入的一种快速方法。


#13楼

如果需要考虑速度,为什么不将最常用的一组输入及其值划分到一个查找表中,然后针对特殊情况做一些优化的魔术算法呢?


#14楼

我希望此函数与所有正64位有符号整数一起使用

Math.sqrt()使用double作为输入参数,因此对于大于2 ^ 53的整数,您将无法获得准确的结果。


#15楼

有人指出,一个完美正方形的最后d位只能取某些值。 当n除以b d ,数字n的最后d数字(以b为底)与其余数相同。 以C表示法n % pow(b, d)

这可以推广到任何模数m ,即。 n % m可用于排除某些百分比的数是完全平方的情况。 您当前使用的模数是64,允许12,即。 余数的19%,尽可能平方。 通过少量编码,我发现了模数110880,它仅允许使用2016年。 余数的1.8%为正方形。 因此,根据模运算(即除法)的成本以及表查找与计算机上的平方根的关系,使用此模可能更快。

顺便说一句,如果Java可以为查询表存储一个打包的位数组,请不要使用它。 如今,110880 32位字已不是太多的RAM,因此,获取机器字比获取单个位要快。


#16楼

仅作记录,另一种方法是使用素数分解。 如果分解的每个因子都是偶数,则该数字是一个完美的平方。 因此,您要查看的是一个数字是否可以分解为质数平方的乘积。 当然,您无需获取这种分解,只需查看它是否存在。

首先建立一个小于2 ^ 32的质数平方的表。 它远远小于所有不超过此限制的整数表格。

一个解决方案将是这样的:

boolean isPerfectSquare(long number)
{if (number < 0) return false;if (number < 2) return true;for (int i = 0; ; i++){long square = squareTable[i];if (square > number) return false;while (number % square == 0){number /= square;}if (number == 1) return true;}
}

我想这有点神秘。 它所做的是在每一步中检查素数平方是否除以输入数。 如果这样做,则只要有可能,就将数字除以平方,以从素数分解中删除该平方。 如果通过此过程,我们得出1,则输入数字是质数平方的分解。 如果平方变得大于数字本身,则该平方或任何更大的平方都无法对其进行划分,因此该数字不能是质数平方的分解。

鉴于当今的sqrt是在硬件中完成的,并且需要在此处计算素数,所以我认为此解决方案的速度要慢得多。 正如mrzl在他的回答中说的那样,但是它比使用sqrt的解决方案提供更好的结果,而sqrt不能在2 ^ 54上工作。


#17楼

为了提高性能,您常常不得不做出一些妥协。 其他人表达了各种方法,但是,您注意到Carmack的破解速度更快,直到N的某些值。然后,您应检查“ n”,如果小于n,则使用Carmack的破解,否则使用其他描述的方法在这里的答案。


#18楼

您应该从一开始就摆脱N的2幂部分。

2nd Edit下面m的神奇表达应为

m = N - (N & (N-1));

而不是书面的

第二编辑结束

m = N & (N-1); // the lawest bit of N
N /= m;
byte = N & 0x0F;
if ((m % 2) || (byte !=1 && byte !=9))return false;

第一次编辑:

小改进:

m = N & (N-1); // the lawest bit of N
N /= m;
if ((m % 2) || (N & 0x07 != 1))return false;

1st结束

现在照常继续。 这样,到浮点部分时,您已经摆脱了2幂部分为奇数(大约一半)的所有数字,然后只考虑剩下的1/8。 即您将浮点数放在数字的6%上。


#19楼

在Ruby中,这是旧的Marchant计算器算法从十进制到二进制的重做(很抱歉,我没有参考),专门针对此问题进行了调整:

def isexactsqrt(v)value = v.absresidue = valueroot = 0onebit = 1onebit <<= 8 while (onebit < residue)onebit >>= 2 while (onebit > residue)while (onebit > 0)x = root + onebitif (residue >= x) thenresidue -= xroot = x + onebitendroot >>= 1onebit >>= 2endreturn (residue == 0)
end

这是类似内容的改进(请不要因为编码风格/气味或笨拙的O / O而对我投反对票-这是至关重要的算法,而C ++不是我的母语)。 在这种情况下,我们正在寻找残基== 0:

#include <iostream>  using namespace std;
typedef unsigned long long int llint;class ISqrt {           // Integer Square Rootllint value;        // Integer whose square root is requiredllint root;         // Result: floor(sqrt(value))llint residue;      // Result: value-root*rootllint onebit, x;    // Working bit, working valuepublic:ISqrt(llint v = 2) {    // ConstructorRoot(v);            // Take the root };llint Root(llint r) {   // Resets and calculates new square rootvalue = r;          // Store inputresidue = value;    // Initialise for subtracting downroot = 0;           // Clear root accumulatoronebit = 1;                 // Calculate start value of counteronebit <<= (8*sizeof(llint)-2);         // Set up counter bit as greatest odd power of 2 while (onebit > residue) {onebit >>= 2; };  // Shift down until just < valuewhile (onebit > 0) {x = root ^ onebit;          // Will check root+1bit (root bit corresponding to onebit is always zero)if (residue >= x) {         // Room to subtract?residue -= x;           // Yes - deduct from residueroot = x + onebit;      // and step root};root >>= 1;onebit >>= 2;};return root;                    };llint Residue() {           // Returns residue from last calculationreturn residue;                 };
};int main() {llint big, i, q, r, v, delta;big = 0; big = (big-1);         // Kludge for "big number"ISqrt b;                            // Make q sqrt generatorfor ( i = big; i > 0 ; i /= 7 ) {   // for several numbersq = b.Root(i);                  // Get the square rootr = b.Residue();                // Get the residuev = q*q+r;                      // Recalc original valuedelta = v-i;                    // And diff, hopefully 0cout << i << ": " << q << " ++ " << r << " V: " << v << " Delta: " << delta << "\n";};return 0;
};

#20楼

我想出一种方法,至少在我的CPU(x86)和编程语言(C / C ++)上比6bits + Cackack + sqrt代码快约35%。 您的结果可能会有所不同,尤其是因为我不知道Java因素将如何发挥作用。

我的方法是三方面的:

  1. 首先,过滤出明显的答案。 这包括负数并查看最后4位。 (我发现查看最后六个没有帮助。)我也对0回答是。(在阅读下面的代码时,请注意我的输入是int64 x 。)

     if( x < 0 || (x&2) || ((x & 7) == 5) || ((x & 11) == 8) ) return false; if( x == 0 ) return true; 
  2. 接下来,检查它是否为平方模255 = 3 * 5 *17。因为这是三个不同素数的乘积,所以残差mod 255中只有约1/8是平方。 但是,根据我的经验,调用模运算符(%)所花费的成本要比获得的收益多,因此我使用涉及255 = 2 ^ 8-1的位技巧来计算残差。 (无论好坏,我都没有使用从单词中读取单个字节的技巧,仅按位与和移位。)
     int64 y = x; y = (y & 4294967295LL) + (y >> 32); y = (y & 65535) + (y >> 16); y = (y & 255) + ((y >> 8) & 255) + (y >> 16); // At this point, y is between 0 and 511. More code can reduce it farther. 

    要实际检查残差是否为正方形,我在预先计算的表中查找答案。

     if( bad255[y] ) return false; // However, I just use a table of size 512 
  3. 最后,尝试使用类似于Hensel引理的方法计算平方根。 (我认为它不直接适用,但可以进行一些修改。)在此之前,我用二进制搜索将2的所有幂除掉:
     if((x & 4294967295LL) == 0) x >>= 32; if((x & 65535) == 0) x >>= 16; if((x & 255) == 0) x >>= 8; if((x & 15) == 0) x >>= 4; if((x & 3) == 0) x >>= 2; 

    在这一点上,我们的数字必须是平方,必须是1模8。

     if((x & 7) != 1) return false; 

    Hensel引理的基本结构如下。 (注意:未经测试的代码;如果无效,请尝试t = 2或8。)

     int64 t = 4, r = 1; t <<= 1; r += ((x - r * r) & t) >> 1; t <<= 1; r += ((x - r * r) & t) >> 1; t <<= 1; r += ((x - r * r) & t) >> 1; // Repeat until t is 2^33 or so. Use a loop if you want. 

    这个想法是,在每次迭代中,您需要在x的“当前”平方根r上加一位。 每个平方根都是2的幂的乘积,即t / 2。 最后,r和t / 2-r将是x以t / 2为模的平方根。 (请注意,如果r是x的平方根,那么-r也是如此。即使对取模数也是如此,但是要注意,对某些数取模,事物的平方根甚至可以超过2;值得注意的是,这包括2的幂。 )由于我们的实际平方根小于2 ^ 32,因此我们可以仅检查r或t / 2-r是否为实平方根。 在我的实际代码中,我使用以下修改后的循环:

     int64 r, t, z; r = start[(x >> 3) & 1023]; do { z = x - r * r; if( z == 0 ) return true; if( z < 0 ) return false; t = z & (-z); r += (z & t) >> 1; if( r > (t >> 1) ) r = t - r; } while( t <= (1LL << 33) ); 

    这里的加速可以通过三种方式获得:预先计算的起始值(相当于循环的约10次迭代),循环的较早退出以及跳过一些t值。 对于最后一部分,我看z = r - x * x ,并将t设置为2的最大幂除以一点技巧。 这使我可以跳过不会影响r值的t值。 在我的情况下,预先计算的起始值选取了“最小正”平方根模8192。

即使这段代码无法为您更快地工作,我希望您喜欢其中包含的一些想法。 随后是完整的,经过测试的代码,包括预先计算的表。

typedef signed long long int int64;int start[1024] =
{1,3,1769,5,1937,1741,7,1451,479,157,9,91,945,659,1817,11,
1983,707,1321,1211,1071,13,1479,405,415,1501,1609,741,15,339,1703,203,
129,1411,873,1669,17,1715,1145,1835,351,1251,887,1573,975,19,1127,395,
1855,1981,425,453,1105,653,327,21,287,93,713,1691,1935,301,551,587,
257,1277,23,763,1903,1075,1799,1877,223,1437,1783,859,1201,621,25,779,
1727,573,471,1979,815,1293,825,363,159,1315,183,27,241,941,601,971,
385,131,919,901,273,435,647,1493,95,29,1417,805,719,1261,1177,1163,
1599,835,1367,315,1361,1933,1977,747,31,1373,1079,1637,1679,1581,1753,1355,
513,1539,1815,1531,1647,205,505,1109,33,1379,521,1627,1457,1901,1767,1547,
1471,1853,1833,1349,559,1523,967,1131,97,35,1975,795,497,1875,1191,1739,
641,1149,1385,133,529,845,1657,725,161,1309,375,37,463,1555,615,1931,
1343,445,937,1083,1617,883,185,1515,225,1443,1225,869,1423,1235,39,1973,
769,259,489,1797,1391,1485,1287,341,289,99,1271,1701,1713,915,537,1781,
1215,963,41,581,303,243,1337,1899,353,1245,329,1563,753,595,1113,1589,
897,1667,407,635,785,1971,135,43,417,1507,1929,731,207,275,1689,1397,
1087,1725,855,1851,1873,397,1607,1813,481,163,567,101,1167,45,1831,1205,
1025,1021,1303,1029,1135,1331,1017,427,545,1181,1033,933,1969,365,1255,1013,
959,317,1751,187,47,1037,455,1429,609,1571,1463,1765,1009,685,679,821,
1153,387,1897,1403,1041,691,1927,811,673,227,137,1499,49,1005,103,629,
831,1091,1449,1477,1967,1677,697,1045,737,1117,1737,667,911,1325,473,437,
1281,1795,1001,261,879,51,775,1195,801,1635,759,165,1871,1645,1049,245,
703,1597,553,955,209,1779,1849,661,865,291,841,997,1265,1965,1625,53,
1409,893,105,1925,1297,589,377,1579,929,1053,1655,1829,305,1811,1895,139,
575,189,343,709,1711,1139,1095,277,993,1699,55,1435,655,1491,1319,331,
1537,515,791,507,623,1229,1529,1963,1057,355,1545,603,1615,1171,743,523,
447,1219,1239,1723,465,499,57,107,1121,989,951,229,1521,851,167,715,
1665,1923,1687,1157,1553,1869,1415,1749,1185,1763,649,1061,561,531,409,907,
319,1469,1961,59,1455,141,1209,491,1249,419,1847,1893,399,211,985,1099,
1793,765,1513,1275,367,1587,263,1365,1313,925,247,1371,1359,109,1561,1291,
191,61,1065,1605,721,781,1735,875,1377,1827,1353,539,1777,429,1959,1483,
1921,643,617,389,1809,947,889,981,1441,483,1143,293,817,749,1383,1675,
63,1347,169,827,1199,1421,583,1259,1505,861,457,1125,143,1069,807,1867,
2047,2045,279,2043,111,307,2041,597,1569,1891,2039,1957,1103,1389,231,2037,
65,1341,727,837,977,2035,569,1643,1633,547,439,1307,2033,1709,345,1845,
1919,637,1175,379,2031,333,903,213,1697,797,1161,475,1073,2029,921,1653,
193,67,1623,1595,943,1395,1721,2027,1761,1955,1335,357,113,1747,1497,1461,
1791,771,2025,1285,145,973,249,171,1825,611,265,1189,847,1427,2023,1269,
321,1475,1577,69,1233,755,1223,1685,1889,733,1865,2021,1807,1107,1447,1077,
1663,1917,1129,1147,1775,1613,1401,555,1953,2019,631,1243,1329,787,871,885,
449,1213,681,1733,687,115,71,1301,2017,675,969,411,369,467,295,693,
1535,509,233,517,401,1843,1543,939,2015,669,1527,421,591,147,281,501,
577,195,215,699,1489,525,1081,917,1951,2013,73,1253,1551,173,857,309,
1407,899,663,1915,1519,1203,391,1323,1887,739,1673,2011,1585,493,1433,117,
705,1603,1111,965,431,1165,1863,533,1823,605,823,1179,625,813,2009,75,
1279,1789,1559,251,657,563,761,1707,1759,1949,777,347,335,1133,1511,267,
833,1085,2007,1467,1745,1805,711,149,1695,803,1719,485,1295,1453,935,459,
1151,381,1641,1413,1263,77,1913,2005,1631,541,119,1317,1841,1773,359,651,
961,323,1193,197,175,1651,441,235,1567,1885,1481,1947,881,2003,217,843,
1023,1027,745,1019,913,717,1031,1621,1503,867,1015,1115,79,1683,793,1035,
1089,1731,297,1861,2001,1011,1593,619,1439,477,585,283,1039,1363,1369,1227,
895,1661,151,645,1007,1357,121,1237,1375,1821,1911,549,1999,1043,1945,1419,
1217,957,599,571,81,371,1351,1003,1311,931,311,1381,1137,723,1575,1611,
767,253,1047,1787,1169,1997,1273,853,1247,413,1289,1883,177,403,999,1803,
1345,451,1495,1093,1839,269,199,1387,1183,1757,1207,1051,783,83,423,1995,
639,1155,1943,123,751,1459,1671,469,1119,995,393,219,1743,237,153,1909,
1473,1859,1705,1339,337,909,953,1771,1055,349,1993,613,1393,557,729,1717,
511,1533,1257,1541,1425,819,519,85,991,1693,503,1445,433,877,1305,1525,
1601,829,809,325,1583,1549,1991,1941,927,1059,1097,1819,527,1197,1881,1333,
383,125,361,891,495,179,633,299,863,285,1399,987,1487,1517,1639,1141,
1729,579,87,1989,593,1907,839,1557,799,1629,201,155,1649,1837,1063,949,
255,1283,535,773,1681,461,1785,683,735,1123,1801,677,689,1939,487,757,
1857,1987,983,443,1327,1267,313,1173,671,221,695,1509,271,1619,89,565,
127,1405,1431,1659,239,1101,1159,1067,607,1565,905,1755,1231,1299,665,373,
1985,701,1879,1221,849,627,1465,789,543,1187,1591,923,1905,979,1241,181};bool bad255[512] =
{0,0,1,1,0,1,1,1,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1,0,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0,1,1,0,1,1,1,1,0,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,1,0,1,1,1,0,1,0,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0};inline bool square( int64 x ) {// Quickfailif( x < 0 || (x&2) || ((x & 7) == 5) || ((x & 11) == 8) )return false;if( x == 0 )return true;// Check mod 255 = 3 * 5 * 17, for funint64 y = x;y = (y & 4294967295LL) + (y >> 32);y = (y & 65535) + (y >> 16);y = (y & 255) + ((y >> 8) & 255) + (y >> 16);if( bad255[y] )return false;// Divide out powers of 4 using binary searchif((x & 4294967295LL) == 0)x >>= 32;if((x & 65535) == 0)x >>= 16;if((x & 255) == 0)x >>= 8;if((x & 15) == 0)x >>= 4;if((x & 3) == 0)x >>= 2;if((x & 7) != 1)return false;// Compute sqrt using something like Hensel's lemmaint64 r, t, z;r = start[(x >> 3) & 1023];do {z = x - r * r;if( z == 0 )return true;if( z < 0 )return false;t = z & (-z);r += (z & t) >> 1;if( r > (t  >> 1) )r = t - r;} while( t <= (1LL << 33) );return false;
}

#21楼

考虑到一般的位长(尽管我在这里使用了特定的类型),我尝试如下设计简单的算法。 最初需要简单明显地检查0,1,2或<0。 在不尝试使用任何现有数学函数的意义上,遵循以下意义很简单。 大多数运算符可以替换为按位运算符。 我还没有测试任何基准数据。 我既不是数学专家,也不是计算机算法设计专家,我很乐意看到您指出问题。 我知道那里有很多改进的机会。

int main()
{unsigned int c1=0 ,c2 = 0;  unsigned int x = 0;  unsigned int p = 0;  int k1 = 0;  scanf("%d",&p);  if(p % 2 == 0) {  x = p/2; }  else {  x = (p/2) +1;  }  while(x) {if((x*x) > p) {  c1 = x;  x = x/2; }else {  c2 = x;  break;  }  }  if((p%2) != 0)  c2++;while(c2 < c1) {  if((c2 * c2 ) == p) {  k1 = 1;  break;  }  c2++; }  if(k1)  printf("\n Perfect square for %d", c2);  else  printf("\n Not perfect but nearest to :%d :", c2);  return 0;
}

#22楼

这是最简单,最简洁的方法,尽管我不知道它在CPU周期方面如何比较。 如果您只想知道根是否为整数,则此方法非常有用。 如果您真的在意它是否是整数,也可以弄清楚。 这是一个简单的函数(纯函数):

public static boolean isRootWhole(double number) {return Math.sqrt(number) % 1 == 0;
}

如果您不需要微优化,那么从简单性和可维护性方面来看,此答案会更好。 如果您将得到负数,则可能要在number参数上使用Math.abs()作为Math.sqrt()参数。

在我的3.6Ghz Intel i7-4790 CPU上,此算法在0-10,000,000上运行,每次计算平均需要35-37纳秒。 我进行了10次连续运行,打印出每千万次计算所花费的平均时间。 每次总运行只花费了600毫秒多一点。

如果执行较少的计算,则较早的计算会花费更长的时间。


#23楼

我不知道这之前是否提到过。 但是我在这里找到了解决方案:

int result = (int)(floor(sqrt(b)) - ceil(sqrt(a)) + 1);

#24楼

只要起始值合理,用牛顿法计算平方根的速度就非常快。 但是,没有合理的起始值,实际上,我们以二等分和log(2 ^ 64)行为结束。
要真正快,我们需要一种快速的方法来获得一个合理的起始值,这意味着我们需要使用机器语言。 如果处理器在奔腾中提供像POPCNT这样的指令,该指令会计算前导零,我们可以使用它来获得一个起始值,该起始值的有效位为一半。 小心地找到一个固定数量的牛顿步骤,将始终满足要求。 (因此无需循环并具有非常快的执行速度。)

第二种解决方案是通过浮点工具进行,该工具可能具有快速的sqrt计算(如i87协处理器)。即使通过exp()和log()进行偏移,也可能比牛顿退化为二进制搜索要快。 这是一个棘手的方面,需要对处理器进行分析,然后再进行精炼。

第三种解决方案解决了一个稍有不同的问题,但是值得一提,因为情况已在问题中进行了描述。 如果要为数量略有不同的数字计算很多平方根,可以使用牛顿迭代法,如果您从不重新初始化起始值,而只需将其保留在上一次计算结束的位置。 在至少一个欧拉问题中,我已经成功地使用了它。


#25楼

这是一个分而治之的解决方案。

如果一个自然数(的平方根number )是一个自然数( solution ),就可以很容易地确定用于各种solution的基础上的数字位数number

  • number有1位数字:范围内的solution = 1-4
  • number有2位数字:范围内的solution = 3-10
  • number有3位数字:范围内的solution = 10-40
  • number有4位数字:范围内的solution = 30-100
  • number有5位数字:范围内的solution = 100-400

注意到重复吗?

您可以在二进制搜索方法中使用此范围,以查看是否存在以下solution

number == solution * solution

这是代码

这是我的课程SquareRootChecker

public class SquareRootChecker {private long number;private long initialLow;private long initialHigh;public SquareRootChecker(long number) {this.number = number;initialLow = 1;initialHigh = 4;if (Long.toString(number).length() % 2 == 0) {initialLow = 3;initialHigh = 10;}for (long i = 0; i < Long.toString(number).length() / 2; i++) {initialLow *= 10;initialHigh *= 10;}if (Long.toString(number).length() % 2 == 0) {initialLow /= 10;initialHigh /=10;}}public boolean checkSquareRoot() {return findSquareRoot(initialLow, initialHigh, number);}private boolean findSquareRoot(long low, long high, long number) {long check = low + (high - low) / 2;if (high >= low) {if (number == check * check) {return true;}else if (number < check * check) {high = check - 1;return findSquareRoot(low, high, number);}else  {low = check + 1;return findSquareRoot(low, high, number);}}return false;}}

这是有关如何使用它的示例。

long number =  1234567;
long square = number * number;
SquareRootChecker squareRootChecker = new SquareRootChecker(square);
System.out.println(square + ": " + squareRootChecker.checkSquareRoot()); //Prints "1524155677489: true"long notSquare = square + 1;
squareRootChecker = new SquareRootChecker(notSquare);
System.out.println(notSquare + ": " + squareRootChecker.checkSquareRoot()); //Prints "1524155677490: false"

#26楼

应该有可能打包“如果最后X个数字为N,就不能成为一个完美的正方形”比这更有效! 我将使用Java 32位整数,并产生足够的数据来检查数字的最后16位-即2048个十六进制整数值。

...

好。 我可能遇到了一些数字理论,而这超出了我本人的理解范围,或者我的代码中存在错误。 无论如何,这是代码:

public static void main(String[] args) {final int BITS = 16;BitSet foo = new BitSet();for(int i = 0; i< (1<<BITS); i++) {int sq = (i*i);sq = sq & ((1<<BITS)-1);foo.set(sq);}System.out.println("int[] mayBeASquare = {");for(int i = 0; i< 1<<(BITS-5); i++) {int kk = 0;for(int j = 0; j<32; j++) {if(foo.get((i << 5) | j)) {kk |= 1<<j;}}System.out.print("0x" + Integer.toHexString(kk) + ", ");if(i%8 == 7) System.out.println();}System.out.println("};");
}

结果如下:

(ed:在prettify.js中由于性能不佳而被删除;请查看修订历史记录以查看。)


#27楼

假设数字是一个完美的平方,则为一个数字的平方根。

复杂度为log(n)

/*** Calculate square root if the given number is a perfect square.* * Approach: Sum of n odd numbers is equals to the square root of n*n, given * that n is a perfect square.** @param number* @return squareRoot*/public static int calculateSquareRoot(int number) {int sum=1;int count =1;int squareRoot=1;while(sum<number) {count+=2;sum+=count;squareRoot++;}return squareRoot;
}

#28楼

关于Carmac方法,似乎只需重复一次就很容易了,这应该使精度的位数翻倍。 毕竟,这是一种极其截断的迭代方法-牛顿法,具有很好的首次猜测。

关于您目前的最佳状态,我看到了两个微观优化:

  • 使用mod255在检查后将检查相对于0进行移动
  • 重新排列四的除权数以跳过所有常规检查(75%)的检查。

即:

// Divide out powers of 4 using binary searchif((n & 0x3L) == 0) {n >>=2;if((n & 0xffffffffL) == 0)n >>= 32;if((n & 0xffffL) == 0)n >>= 16;if((n & 0xffL) == 0)n >>= 8;if((n & 0xfL) == 0)n >>= 4;if((n & 0x3L) == 0)n >>= 2;
}

甚至更好可能是一个简单的

while ((n & 0x03L) == 0) n >>= 2;

显然,知道每个检查点剔除多少个数字会很有趣-我相当怀疑这些检查是否真正独立,这使事情变得棘手。


#29楼

如前所述,sqrt调用并不十分准确,但有趣的是,它不会在速度方面使其他答案消失。 毕竟,sqrt的汇编语言指令序列很小。 英特尔拥有硬件指令,我相信Java不会使用该指令,因为它不符合IEEE。

那为什么慢呢? 因为Java实际上是通过JNI调用C例程,所以这样做实际上比调用Java子例程要慢,而Java子例程本身比内联调用要慢。 这非常烦人,Java应该提供更好的解决方案,即在必要时构建浮点库调用。 那好吧。

在C ++中,我怀疑所有复杂的选择都将失去速度,但是我还没有检查全部。 我所做的以及Java人民会发现有用的东西是一个简单的技巧,是A. Rex建议的特殊情况测试的扩展。 使用单个long值作为位数组,不检查边界。 这样,您就可以进行64位布尔查询。

typedef unsigned long long UVLONG
UVLONG pp1,pp2;void init2() {for (int i = 0; i < 64; i++) {for (int j = 0; j < 64; j++)if (isPerfectSquare(i * 64 + j)) {pp1 |= (1 << j);pp2 |= (1 << i);break;}}cout << "pp1=" << pp1 << "," << pp2 << "\n";
}inline bool isPerfectSquare5(UVLONG x) {return pp1 & (1 << (x & 0x3F)) ? isPerfectSquare(x) : false;
}

例程isPerfectSquare5在我的core2 duo机器上运行时间大约为1/3。 我怀疑沿着相同的路线进行进一步的调整平均可以减少时间,但是每次检查时,您都在权衡更多的测试以消除更多的故障,因此您在这条路上不能走得更远。

当然,您可以单独检查高6位,而不用单独测试是否为负。

请注意,我正在做的是消除可能的平方,但是当我有一个可能的情况时,我必须调用原始的内联isPerfectSquare。

一次调用init2例程以初始化pp1和pp2的静态值。 请注意,在我的C ++实现中,我使用了long long long,因此,由于您已签名,因此必须使用>>>运算符。

根本没有必要对数组进行边界检查,但是Java的优化程序必须很快地弄清这些东西,因此我不为此而怪罪。


#30楼

标签中提到了Euler项目,其中的许多问题都需要检查数字>> 2^64 。 当使用80字节缓冲区时,上面提到的大多数优化都不容易。

我使用了Java BigInteger和牛顿方法的稍微修改后的版本,该方法对整数的使用效果更好。 问题在于确切的平方n^2收敛到(n-1)而不是n因为n^2-1 = (n-1)(n+1) ,并且最终误差仅比最终除数和算法终止。 通过在计算错误之前将一个参数添加到原始参数很容易解决。 (为多维数据集根添加两个,等等。)

该算法的一个不错的属性是,您可以立即判断该数字是否是一个完美的平方-牛顿方法的最终误差(而不是校正值)将为零。 一个简单的修改也使您可以快速计算floor(sqrt(x))而不是最接近的整数。 这在遇到几个Euler问题时很方便。


#31楼

当观察到正方形的最后n位时,我检查了所有可能的结果。 通过连续检查更多位,可以消除多达5/6的输入。 我实际上是设计用来实现Fermat的因式分解算法的,在那儿它非常快。

public static boolean isSquare(final long val) {if ((val & 2) == 2 || (val & 7) == 5) {return false;}if ((val & 11) == 8 || (val & 31) == 20) {return false;}if ((val & 47) == 32 || (val & 127) == 80) {return false;}if ((val & 191) == 128 || (val & 511) == 320) {return false;}// if((val & a == b) || (val & c == d){//   return false;// }if (!modSq[(int) (val % modSq.length)]) {return false;}final long root = (long) Math.sqrt(val);return root * root == val;
}

伪代码的最后一位可用于扩展测试以消除更多的值。 上面的测试是针对k = 0、1、2、3

a的形式为(3 << 2k)-1 b的形式为(2 << 2k) c的形式为(2 << 2k + 2)-1 d的形式为(2 << 2k-1)* 10

它首先测试它是否具有平方幂为2的平方残差,然后基于最终模数进行测试,然后使用Math.sqrt进行最终测试。 我从高层职位提出了这个想法,并试图将其扩展。 我感谢任何意见或建议。

更新:使用模数(modSq)和模数基数为44352进行的测试,我的测试在OP更新中一次运行的96%的时间内运行,最大数量为1,000,000,000。


#32楼

我喜欢在某些输入上使用几乎正确的方法的想法。 这是“偏移量”较高的版本。 该代码似乎可以正常工作,并通过了我的简单测试用例。

只需更换:

if(n < 410881L){...}

与此代码:

if (n < 11043908100L) {//John Carmack hack, converted to Java.// See: http://www.codemaestro.com/reviews/9int i;float x2, y;x2 = n * 0.5F;y = n;i = Float.floatToRawIntBits(y);//using the magic number from //http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf//since it more accuratei = 0x5f375a86 - (i >> 1);y = Float.intBitsToFloat(i);y = y * (1.5F - (x2 * y * y));y = y * (1.5F - (x2 * y * y)); //Newton iteration, more accuratesqrt = Math.round(1.0F / y);
} else {//Carmack hack gives incorrect answer for n >= 11043908100.sqrt = (long) Math.sqrt(n);
}

确定整数的平方根是否为整数的最快方法相关推荐

  1. 二分搜索之x平方根(保留整数)

    实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例 1: 输入: 4 输出: 2 示例 ...

  2. C语言学习之从键盘输入一个小于1000的正数,要求输出它的平方根(如平方根不是整数,则输出其整数部分)

    从键盘输入一个小于1000的正数,要求输出它的平方根(如平方根不是整数,则输出其整数部分).要求在输入数据后先对其进行检查是否为小于1000 的正数.若不是,则要求重新输人. # include &l ...

  3. java怎么求两组整数的或集,确定整数是否在具有已知值集的两个整数(包括)之间的最快方法...

    在C或C中是否有比 x >= start && x <= end 更快的方法来测试整数是否在两个整数之间? 更新:我的特定平台是iOS . 这是盒子模糊功能的一部分,它将像 ...

  4. 计算机组成原理整数乘法,计算机组成原理 - 定点整数的原码补码运算(待验证)...

    计算机组成原理 - 定点整数的原码补码运算(待验证) 目录 〇.环境 对象 运算 定点整数原码.定点整数补码 移位.加.减.乘.除 原码定义: \(x=\begin{cases} x &0\l ...

  5. 浮点数和整数的区别python_Python中整数和浮点数

    Python支持对整数和浮点数直接进行四则混合运算,运算规则和数学上的四则运算规则完全一致. 基本的运算: 1 + 2 + 3 # ==> 6 4 * 5 - 6 # ==> 14 7.5 ...

  6. C++两个整数的总和是否为整数溢出的算法实现(附完整源码)

    C++两个整数的总和是否为整数溢出的算法实现 C++两个整数的总和是否为整数溢出的算法实现完整源码(定义,实现,main函数测试) C++两个整数的总和是否为整数溢出的算法实现完整源码(定义,实现,m ...

  7. 给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个。

    资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个. 输入格式 第一行包含一个整数n. 第二行包含n个非负整数,为给定的 ...

  8. Java黑皮书课后题第5章:*5.44(计算机体系结构:比特级的操作)一个short型值用16位比特存储。编写程序,提示用户输入一个short型的整数,然后显示这个整数的16比特形式,下面是运行示例

    5.44(计算机体系结构:比特级的操作)一个short型值用16位比特存储.编写程序,提示用户输入一个short型的整数,然后显示这个整数的16比特形式 题目 题目概述 运行示例 资料与破题 原码反码 ...

  9. Java黑皮书课后题第2章:2.6(求一个整数各位数的和)读取一个0和1000之间的整数,并将给整数的各位数字相加

    2.6(求一个整数各位数的和)读取一个0和1000之间的整数,并将给整数的各位数字相加 题目 题目描述 运行示例 题目槽点与破题 题目槽点 破题 如何提取数字 代码块 方法评价 题目 题目描述 2.6 ...

最新文章

  1. window下实现在线预览功能
  2. 2021广西高考成绩几点可以查询,高考完多久分数能出来广西 2021年广西高考分数查询公布时间...
  3. JUC里面的相关分类|| java并发编程中,关于锁的实现方式有两种synchronized ,Lock || Lock——ReentrantLock||AQS(抽象队列同步器)
  4. Web前端开发笔记——第二章 HTML语言 第十一节 语义标签
  5. 算法:用户喜好--Map与List配合下的查找
  6. python while九九乘法表儿歌_python使用while循环实现九九乘法表
  7. 安全界“圣经”DBIR 报告推翻了哪些“你以为的”数据泄漏情况?
  8. Vue.js学习系列(八)---使用路由搭建单页应用(一)
  9. 奔腾4 2.4CPU计算机硬件能升级吗,CPU怎么升级啊
  10. 过拟合的原因以及如何解决
  11. 1.两数之和(力扣leetcode) 博主可答疑该问题
  12. 固高控制卡Home捕获和Index捕获
  13. NVIDIA显卡驱动的重装
  14. ros nh.parmam 用法
  15. win7设置护眼模式
  16. Arcpy处理月NDVI,最大合成法合成年NDVI
  17. C语言:指针三(线性表的存储结构)
  18. iOS越狱并安装ssl kill switch解除ssl pining,抓取https包
  19. 正则表达式-贪婪匹配与懒惰匹配之获取短信验证码
  20. B2C电子商务系统研发

热门文章

  1. Android Context 详解
  2. Android-Binder进程间通讯机制-多图详解
  3. python词云去除词_使用Python制作一个带GUI界面的词云自动生成工具(连载五)
  4. redis全分布式集群
  5. (0001) iOS 开发之收集第三方资源篇
  6. 阿里面试题,为什么wait()方法要放在同步块中?
  7. win10安装MAYA失败,怎么强力卸载删除注册表并重新安装
  8. JDBC——jdbcUtils加载配置文件赋值
  9. springmvc和struts的区别
  10. linux视频学习6(mysql的安装/)