Java中BigDecimal的幂运算问题

3

我试图获取一个指数非常大的双精度值的幂(例如Java BigInteger可以包含该指数,如10 ^ 30)。

也就是说,我想找到类似于1.75 ^(10 ^ 30)1.23 ^(34234534534222)的东西。如果输出太大,则通过取模素数如10 ^ 9 + 7来进行修改。

如果我想找到一个 Integer 的幂,我可以使用 BigInteger.modPow()方法,该方法接受 BigInteger 参数:

( BigInteger modPow(BigInteger exponent, BigInteger m) )

据我所知,这是Java方面的内容。
new BigDecimal("1.5").pow(1000); // .pow() can get only integers as a parameter , but i want to pass a big number like a BigInteger 

我在Java中找不到类似于BigDecimal的(BigInteger.modPow())等效方法,或者我可能错过了。

有没有办法计算浮点数(Decimal)的大幂?

输入和输出示例:

输入:num //或1.5或任何十进制数字。也可以是整数。

指数:exp //大整数或长整型值

输出:num^exp // num的exp次方

即如何计算1.23^(34234534534222)

如果输出太大,请通过获取10 ^ 9 + 7的质数模数进行修改。


2
我认为你可能会得到一个意外的结果 - 因为BigDecimal支持的只有32位整数 - 但是你仍然可以使用快速指数(http://en.wikipedia.org/wiki/Exponentiation_by_squaring)自己去实现。 - Gábor Bakos
“输出过大”具体指什么时候? - Marco13
@Marco13 当输出是一个数字且太大时,就会出现这种情况。包含太多位数。通过获取模数,我们可以将其保持在较低的限制范围内。在这里,正如我所说,输出不会超过10^9+7。 - prime
1
这并不是这样的。当你有一个值,比如 0.14534523462656491590485,并对其取模一个整数时,它仍然是 0.14534523462656491590485。只有当数字变得太大时,才会减少数字的位数,但你无法通过这种方式摆脱小数位。 - Marco13
将失去最低有效数字的“double”与依赖它们的模数相结合,肯定是错误的。例如,计算(double) 1.23 ** (34234534534222) % p毫无意义,因为1.23不能被准确地表示为double,你得到的基本上是一个随机数。如果double再多一位精度,你会得到一个完全不相关的垃圾数字。++我猜,这是为了欧拉项目,那么你必须摆脱使用“double”。 - maaartinus
2个回答

1

有一个 核心数学函数的 Math.BigDecimal 实现,它具有以下特点:

static java.math.BigDecimal powRound(java.math.BigDecimal x, java.math.BigInteger n) 
          Raise to an integer power and round.

这似乎正是你需要的。事实上,有一个外部库可以使用,这意味着在java.Math中没有这样的核心实现方法。

顺便说一句,如果你的输入在小数位方面相对较小(因此不是无理数),就像1.5一样,你可以将其转换为15/10并执行操作。

(15^BigInteger)/(10^BigInteger)

使用BigIntegermodPow(BigInteger exponent, BigInteger m),这显然会增加复杂度和计算数字。

0

有几个注意事项。正如Gábor Bakos所指出的那样,结果值很可能包含太多位数,甚至无法表示为BigDecimal

此外,这些数字的位数增长得非常快,因此计算像2.034234534534222这样的东西在存储方面完全超出了范围(并且,我认为,在所需时间方面也是如此)。

您提到当值变得“太大”时,可以对其进行大质数取模运算。虽然您没有说清楚这意味着什么,但这并不一定会帮助您,因为使用取模运算将不会截断小数位。您必须以某种方式限制计算精度。

然而,使用平方法求幂的最简单实现可能看起来像这样:大致

import java.math.BigDecimal;
import java.math.BigInteger;

public class BigDecimalPow {
    public static void main(String[] args) {
        BigDecimal b = new BigDecimal(1.5);
        BigInteger e = new BigInteger("325322");
        BigDecimal result = pow(b, e);
        System.out.println("Done "+result.scale());
        System.out.println(result);
    }


    /**
     * Computes d to the power of e
     * @param b The value
     * @param e The exponent
     * @return The power
     */
    private static BigDecimal pow(BigDecimal b, BigInteger e) {
        BigDecimal result = BigDecimal.ONE;
        BigDecimal p = b;
        int skipped = 0;
        while (e.compareTo(BigInteger.ZERO) > 0) {
            if (e.and(BigInteger.ONE).equals(BigInteger.ONE)) {
                if (skipped > 0) {

                    if (skipped > 29) {
                        p = pow(p, BigInteger.ONE.shiftLeft(skipped));
                    } else {
                        p = p.pow(1 << skipped);
                    }
                    skipped = 0;
                }
                result = result.multiply(p);
            }
            skipped++;
            e = e.shiftRight(1);
            System.out.println(e);
        }
        return result;
    }

}

注意:上面的实现非常简单。对于某些情况,可能有更有效的解决方案,或者使用模运算来支持“更大”的数字。但是,除非您拥有34TB的RAM和具有long寻址的JVM,否则您无法表示(潜在地)34234534534222个十进制位数,因此我怀疑在目前为止没有满足您所述要求的解决方案 - 但是如果有人证明我错了,我会点赞+奖励...

有一个类似的问题,你可以帮我解决吗? https://stackoverflow.com/questions/45768020/finding-the-modulor-of-large-numbers - Narendra Modi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接