检查长整数是否为多维数据集的快速方法(在Java中)

kon*_*wka 3 java algorithm math numbers cubes

我正在编写一个程序,其中我需要检查某些大数字(立方体的排列)是否为立方(对于某些n而言等于n ^ 3).

目前我只是使用这种方法

static boolean isCube(long input) {
    double cubeRoot = Math.pow(input,1.0/3.0);
    return Math.round(cubeRoot) == cubeRoot;
}
Run Code Online (Sandbox Code Playgroud)

但是当使用大数字(10位数)时,这非常慢.有没有更快的方法来确定整数是否是立方体?

Dav*_*tat 6

只有2 ^ 21个多维数据集不会溢出很长(如果允许负数,则为2 ^ 22 - 1),因此您可以使用HashSet查找.


A. *_*ebb 6

您可以通过测试模数给定的数字来消除大量候选人.例如,以数字为模的立方体819只能采用以下45值.

 0  125  181  818  720  811  532  755  476
 1  216   90  307  377  694  350  567  442
 8  343  559  629  658  351  190   91  469
27  512  287  252  638  118  603  161  441
64  729   99  701  792  378  260  468  728
Run Code Online (Sandbox Code Playgroud)

因此,您可以消除实际上必须在几乎95%的均匀分布情况下计算立方根.

  • @Stas 819 = 9*7*13,因此Z/819 = Z/9 x Z/7 x Z/13。选择这些素数是因为一般来说,Z/p^n 的乘法群是 (p-1)*p^{n-1} 阶的循环群。如果该阶数能被 3 整除,则这些元素中只有 1/3 可以是立方体。因此,唯一有帮助的方法是 n=2、p=3(任何更高的 n 都没有帮助)或 p == 1 (mod 3)。前两个素数 == 1 (mod 3) 是 7 和 13。如果您想要更好的百分比,可以乘以 19(下一个素数 == 1 (mod 3))。 (2认同)

Pet*_*des 6

黑客的喜悦书有整数立方根这可能是值得移植到64位多头短暂而快速的功能,请参阅下文.


似乎测试一个数字是一个完美的立方体可以比实际计算立方根更快. Burningmath有一种使用" 数字根 "的技术(对数字求和.重复直到它是一个数字).如果数字根是0,1或8,您的数字可能是一个完美的立方体.

这种方法对于置换(数字?)数字的情况非常有价值.如果您可以通过其数字根排除数字,则排除所有排列.

他们还描述了一种基于检查完美立方体的主要因素的技术.这看起来最适合心算,因为我认为因子分解比计算机上的立方体生根要慢.

无论如何,数字根对计算机来说很快,你甚至可以将你的数字作为一串数字来开始.你仍然需要一个10分频的循环,但你的起点是输入数字的总和,而不是整数,所以它不会有很多分歧.(整数除法比当前CPU的乘法慢一个数量级,但除以编译时常量可以优化乘以+移位与定点逆.希望Java JIT编译器也使用它,也许甚至将它用于运行时常量.)

这加上A.韦伯的测试(input % 819- >搜索45个条目的表格)将排除很多输入,因为不可能是完美的立方体.IDK,如果二进制搜索,线性搜索或散列/集合最好.

这些测试可能是David Eisenstat关于long在数据结构中存储完美立方体的s 集合的想法的前端,该数据结构允许快速存在检查.(例如HashSet).是的,缓存未命中是非常昂贵的,至少在进行HashSet查找之前,数字根测试可能是值得的,可能两者都有.

通过将它用于Bloom Filter而不是精确集(David Ehrman的建议),你可以在这个想法上使用更少的内存.这将为完整计算提供另一个候选拒绝前端.所述guavac BloomFilter实施需要一个"漏斗"的功能来翻译对象字节,在这种情况下,应F(X)= X).

我怀疑Bloom过滤不会是对完全HashSet检查的重大胜利,因为它需要多次内存访问.当你真的无法负担全桌的空间时,这是合适的,你过滤掉的东西就像磁盘访问一样非常昂贵.

整数立方根函数(下面)可能比单个缓存未命中更快.如果cbrt检查导致缓存未命中,那么当其数据被驱逐时,其余代码可能也会遭受更多缓存未命中.


Math.SE 对于完美的正方形有一个问题,但那是关于正方形,而不是立方体,所以这些都没有出现.然而,那里的答案确实讨论并避免了方法中的问题.> <

您的方法有几个问题:


FP立方根,然后测试生成的整数,避免了FP比较引入的所有问题:

static boolean isCube(long input) {
    double cubeRoot = Math.cbrt(input);
    long intRoot = Math.round(cubeRoot);
    return (intRoot*intRoot*intRoot) == input;
}
Run Code Online (Sandbox Code Playgroud)

(在搜索之后,我看到其他人在其他stackoverflow/stackexchange答案上也提出了整数比较方法.)

如果您需要高性能,并且不介意使用更多源代码的更复杂的功能,那么有可能.例如,使用具有整数数学的立方根逐次逼近算法.如果你最终到达n^3 < input <(n + 1)^ 3 , then输入`不是立方体的点.

关于这个math.SE问题的方法有一些讨论.

我不打算花时间详细研究整数立方根算法,因为该cbrt部分可能不是主要的瓶颈.可能输入解析和字符串 - >长转换是您瓶颈的主要部分.

实际上,我很好奇.事实证明,Hacker's Delight中已经存在整数立方根实现(即使没有归属也允许使用/复制/分发.AFAICT,它本质上是公共域代码.):

// Hacker's delight integer cube-root (for 32-bit integers, I think)
int icbrt1(unsigned x) {
   int s;
   unsigned y, b;

   y = 0;
   for (s = 30; s >= 0; s = s - 3) {
      y = 2*y;
      b = (3*y*(y + 1) + 1) << s;
      if (x >= b) {
         x = x - b;
         y = y + 1;
      }
   }
   return y;
}
Run Code Online (Sandbox Code Playgroud)

30看起来像一个基于一个位数的幻数int.将其移植到long需要测试.(另请注意,这是C,但看起来它也应该用Java编译!)

IDK,如果这是Java人员的常识,但32位Windows JVM不使用serverJIT引擎,也不优化您的代码.