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

时间:2015-08-14 19:23:15

标签: 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;
}

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

4 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:6)

Hacker's Delight本书对整数立方根有一个短而快的函数,值得移植到64位长,见下文。

似乎测试一个数字是一个完美的立方体可以比实际计算立方根更快。 Burningmath has a technique使用" digital root" (将数字相加。重复直到它为一位数)。如果数字根是0,1或8,那么您的数字可能是一个完美的立方体。

这种方法对于您的置换(数字?)数字的情况非常有价值。如果你可以通过数字根排除数字,那么也排除了所有排列。

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

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

此加A. Webb's testinput % 819 - >搜索包含45个条目的表格)将排除输入的 lot 作为不可能完美的多维数据集。 IDK,如果二进制搜索,线性搜索或散列/集合最好。

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

通过将Bloom Filter用于David Ehrman's suggestion而不是精确集(guavac BloomFilter implementation),您可以使用更少的内存。这将为完整计算提供另一个候选拒绝前端。 question about this for perfect squares需要一个"漏斗"将对象转换为字节的函数,在本例中应为f(x)= x)。

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

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

Math.SE有一个The problem with using pow(x, 1./3) is that 1/3 does not have an exact representation in floating point, so you're not "really" getting the cube root.,但那是关于正方形,而不是立方体,所以没有出现这些。然而,那里的答案确实讨论并避免了方法中的问题。 >。<

您的方法存在以下几个问题:

  • Java docs因此请使用cbrt。它不太可能变慢,除非它具有更高的准确性和时间成本。

  • 您假设Math.powMath.cbrt始终返回一个完整的整数值,而不是41.999999或其他值。 What Every Computer Scientist Should Know About Floating-Point Arithmetic说:

    计算结果必须在精确结果的1 ulp范围内。

    这意味着您的代码可能无法在符合标准的Java实现上运行。 比较完全相同的浮点数是棘手的业务Comparing Floating Point Numbers, 2012 Edition对浮点有很多话要说,但它真的很长。 (有充分的理由。用浮点射击自己很容易。)参见this math.SE question,布鲁斯道森的一系列FP文章。

  • 我认为它不适用于所有long值。 double只能精确地表示高达2 ^ 53的整数(64位IEEE双尾数中的尾数)。不能精确表示的整数Math.cbrt甚至不太可能是精确整数。

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

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

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

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

Hacker's Delight的方法进行了一些讨论。

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

实际上,我很好奇。事实证明,even without attribution is allowed中已经存在整数多维数据集根实现(使用/复制/分发{{3}} .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;
}

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

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

答案 2 :(得分:0)

如果将int更改为long并从30更改为60,则骇客的例程似乎对长整数有效。如果将30更改为61,则似乎无效。

我不太了解该程序,所以我制作了另一个似乎可以在Java中运行的版本。

private static int cubeRoot(long n) {
    final int MAX_POWER = 21;
    int power = MAX_POWER;
    long factor;
    long root = 0;
    long next, square, cube;

    while (power >= 0) {
        factor = 1 << power;
        next = root + factor;
        while (true) {
            if (next > n) {
                break;
            }
            if (n / next < next) {
                break;
            }
            square = next * next;
            if (n / square < next) {
                break;
            }
            cube = square * next;
            if (cube > n) {
                break;
            }
            root = next;
            next += factor;
        }
        --power;
    }
    return (int) root;
}

答案 3 :(得分:-1)

请定义非常显示。这是一个测试程序:

public static void main(String[] args) {
    for (long v = 1; v > 0; v = v * 10) {
        long start = System.nanoTime();
        for (int i = 0; i < 100; i++)
            isCube(v);
        long end = System.nanoTime();
        System.out.println(v + ": " + (end - start) + "ns");
    }
}
static boolean isCube(long input) {
    double cubeRoot = Math.pow(input,1.0/3.0);
    return Math.round(cubeRoot) == cubeRoot;
}

输出是:

1: 290528ns
10: 46188ns
100: 45332ns
1000: 46188ns
10000: 46188ns
100000: 46473ns
1000000: 46188ns
10000000: 45048ns
100000000: 45048ns
1000000000: 44763ns
10000000000: 45048ns
100000000000: 44477ns
1000000000000: 45047ns
10000000000000: 46473ns
100000000000000: 47044ns
1000000000000000: 46188ns
10000000000000000: 65291ns
100000000000000000: 45047ns
1000000000000000000: 44477ns

我没有看到&#34;大&#34;号。