不使用Math.BigInteger的高功率模数

时间:2017-01-02 14:09:28

标签: java math modulo exponent

我必须将此公式转换为java代码:

如果我可以使用像Math.BigInteger这样的库会更容易,但不幸的是我应该在没有它的情况下这样做。 stackoverflow上的一些类似问题建议编写一个自己的bignum库,但我想在没有它的情况下这样做。

现在,我的进展是在这一步:

int h(String s) {
  long value = 1;
  int mod = ht.length;

  for (int i=0; i < s.length()-1; i++) {
     h += s.charAt(i) * Math.pow(256,i);
  }
  return (int) h % mod;
}

我知道功率值在整数范围内变得非常快,所以我想到编写一个自己的方法来计算值的功效和模数。我的数学知识还不足以知道何时使用模数以及如何轻松实现。

提前致谢!

4 个答案:

答案 0 :(得分:5)

如果你从后面走,你根本不需要任何权力。在每一步简单地乘以256将产生相同的效果(后面的值“累积”更多的乘法,将它们提升到所需的功率)。例如(未经测试)

int h(String s) {
  int res = 0;
  int n = ht.length;

  for (int i = s.length() - 1; i >= 0; i--) {
     // using a long here to prevent premature wrapping
     long t = res * 256L + s.charAt(i);
     res = (int)(t % n);
  }
  return (res + 1) % n;
}

另请注意,ht.length不应该是2的幂(因此您不能跳过循环中的模数减少,如果ht.length ,则可以2的力量,因为如果它是2的幂,则哈希依赖于(最多)前4个字符,这显然是坏的。

答案 1 :(得分:3)

我默认为你的n选择了一个很大的素数,问你的导师,但是使用任何非素数都不是一个好主意。如果这是哈希表中的桶数,请确保该数字是素数。另外,你的for循环退出条件中你不能-1,你错过了最后一个字符。

private static int MAX_PRIME = 2147483647; //largest positive 32 signed int prime (also happens to be the largest positive 32 signed int)

public static int hash(String s) {
    return hash(s, MAX_PRIME);
}

public static int hash(String s, int primeN) {
    long h = 1;
    long m = 1;

    for (int i = 0; i < s.length(); i++) {
        h += s.charAt(i) * m;
        h %= primeN;
        m *= 256;
        m %= primeN;
    }

    return (int) h;
}

如果要测试正确性,则可以将生成的哈希值与BigInteger实现进行比较:

public static int hashBigInt(String s) {
    return hashBigInt(s, MAX_PRIME);
}

public static int hashBigInt(String s, int primeN) {
    final BigInteger bi256 = BigInteger.valueOf(256);
    BigInteger h = BigInteger.ONE;
    BigInteger m = BigInteger.ONE;

    for (int i = 0; i < s.length(); i++) {
        h = h.add(BigInteger.valueOf(s.charAt(i)).multiply(m));
        m = m.multiply(bi256);
    }

    return h.mod(BigInteger.valueOf(primeN))
            .intValue();
}

答案 2 :(得分:2)

基本上你应该将(a + b) % n = (a % n + b % n) % n深入到等式中,以便在每一步都保持低值。为此,您基本上可以使用模块规则:

  • (a * b) % n = (a % n * b % n) % n
  • h = (h + s.charAt(i) * Math.pow(256, i)) % mod;

首先将其移入循环:

pow

然后将其移至h = (h + s.charAt(i) * Math.pow(256 % mod, i)) % mod;

pow

最后,我会停止使用((((256 % mod) * 256 % mod) * 256 % mod) ... ),所以在每个步骤之后你需要修改一些自定义功能 {{1}}

答案 3 :(得分:1)

我认为你正在考虑快速modular exponentiation

让我们考虑这个简单的公式来解释它是如何工作的:

x = A^B % Cx = 5^117 % 19

1。将B分解为2的幂

117 = (2^0 + 2^2 + 2^4 + 2^5 + 2^6) 117 = (1 + 4 + 16 + 32 + 64 )

2。计算功率为2的&lt; = B

的模数C.
5^2 mod 19 = (5^1 * 5^1) mod 19 = (5^1 mod 19 * 5^1 mod 19) mod 19
5^2 mod 19 = (5 * 5) mod 19 = 25 mod 19
5^2 mod 19 = 6

5^4 mod 19 = (5^2 * 5^2) mod 19 = (5^2 mod 19 * 5^2 mod 19) mod 19
5^4 mod 19 = (6 * 6) mod 19 = 36 mod 19
5^4 mod 19 = 17

5^8 mod 19 = (5^4 * 5^4) mod 19 = (5^4 mod 19 * 5^4 mod 19) mod 19
5^8 mod 19 = (17 * 17) mod 19 = 289 mod 19
5^8 mod 19 = 4

5^16 mod 19 = (5^8 * 5^8) mod 19 = (5^8 mod 19 * 5^8 mod 19) mod 19
5^16 mod 19 = (4 * 4) mod 19 = 16 mod 19
5^16 mod 19 = 16

5^32 mod 19 = (5^16 * 5^16) mod 19 = (5^16 mod 19 * 5^16 mod 19) mod 19
5^32 mod 19 = (16 * 16) mod 19 = 256 mod 19
5^32 mod 19 = 9

5^64 mod 19 = (5^32 * 5^32) mod 19 = (5^32 mod 19 * 5^32 mod 19) mod 19
5^64 mod 19 = (9 * 9) mod 19 = 81 mod 19
5^64 mod 19 = 5

3。计算X

5^117 mod 19 = ( 5^1 * 5^4 * 5^16 * 5^32 * 5^64) mod 19
5^117 mod 19 = ( 5^1 mod 19 * 5^4 mod 19 * 5^16 mod 19 * 5^32 mod 19 * 5^64 mod 19) mod 19
5^117 mod 19 = ( 5 * 17 * 16 * 9 * 5 ) mod 19
5^117 mod 19 = 61200 mod 19 = 1
5^117 mod 19 = 1

为什么这会解决您的问题

您的A或B可能超过Integer限制。

不是将所有值相加,然后最后应用模数,你可以总结到整数限制,然后应用上面的公式,然后重新开始求和,重新应用公式,依此类推,因为6 % 4 == (3 % 4) + (3 % 4) % 4 < / p>