如何计算2的幂N,其中N是非常大的数字

时间:2019-06-20 13:29:40

标签: java biginteger pow

我需要找到2个幂幂N,其中N是一个非常大的数字(Java BigInteger类型)

Java BigInteger类具有pow方法,但仅将整数值作为指数。

因此,我编写了如下方法:

static BigInteger twoToThePower(BigInteger n)
   {
      BigInteger result = BigInteger.valueOf(1L);

      while (n.compareTo(BigInteger.valueOf((long) Integer.MAX_VALUE)) > 0)
      {
         result = result.shiftLeft(Integer.MAX_VALUE);
         n = n.subtract(BigInteger.valueOf((long) Integer.MAX_VALUE));

      }

      long k = n.longValue();
      result = result.shiftLeft((int) k);

      return result;
   }

我的代码运行良好,我只是在分享我的想法,并想知道是否还有其他更好的想法?

谢谢。

4 个答案:

答案 0 :(得分:2)

您不能使用BigInteger存储计算结果。从javadoc中:

  

BigInteger必须支持-2 ^ Integer.MAX_VALUE(不包括)到+ 2 ^ Integer.MAX_VALUE(不包括)之间的值,并且可以支持该范围之外的值。

这是pow方法采用int的原因。在我的计算机上,BigInteger.ONE.shiftLeft(Integer.MAX_VALUE)抛出java.lang.ArithmeticException(消息为“ BigInteger会溢出支持的范围”)。

答案 1 :(得分:1)

通过重复平方,可以实现您的目标。我在下面的示例代码中发布了代码,以了解重复平方的逻辑。

static BigInteger pow(BigInteger base, BigInteger exponent) {
    BigInteger result = BigInteger.ONE;
    while (exponent.signum() > 0) {
        if (exponent.testBit(0)) result = result.multiply(base);
        base = base.multiply(base);
        exponent = exponent.shiftRight(1);
    }
    return result;
}

答案 2 :(得分:1)

一个有趣的问题。只是为了向公认的答案中添加更多信息,检查BigInteger的openjdk 8源代码会发现这些位存储在数组final int[] mag;中。由于数组最多可以包含Integer.MAX_VALUE个元素,因此这立即对BigInteger的2 (32 * Integer.MAX_VALUE)的特定实现施加了理论上的限制。因此,即使您反复进行左移的方法也只能超过一个int的大小最多32倍。

那么,您准备好生成自己的BigInteger实现了吗?

答案 3 :(得分:1)

伊曼纽尔·隆卡(Emmanuel Lonca)的答案是正确的。但是,根据Manoj Banik的想法,我也想分享我的想法。

我的代码与Manoj Banik的代码做得更快。这个想法是初始化缓冲区,并将位1放入正确的位置。我在1个字节上使用了左移运算符,而不是shiftLeft方法。
这是我的代码:

    static BigInteger twoToThePower(BigInteger n){
        BigInteger eight = BigInteger.valueOf(8);
        BigInteger[] devideResult = n.divideAndRemainder(eight);
        BigInteger bufferSize = devideResult[0].add(BigInteger.ONE);
        int  offset = devideResult[1].intValue();
        byte[] buffer = new byte[bufferSize.intValueExact()];
        buffer[0] = (byte)(1 << offset);
        return new BigInteger(1,buffer);
    }

但是它仍然比BigInteger.pow

然后,我发现类BigInteger有一个名为setBit的方法。它还像int方法一样接受参数类型pow。使用此方法比BigInteger.pow快。
该代码可以是:

    static BigInteger twoToThePower(BigInteger n){
        return BigInteger.ZERO.setBit(n.intValueExact());
    }

BigInteger也具有一种称为modPow的方法。但是它还需要一个参数。这意味着您应指定modulus,并且结果应小于此modulus。我没有对modPow进行性能测试,但我认为它应该比pow方法慢。