用 % 处理溢出

发布于 2024-12-11 04:15:20 字数 423 浏览 0 评论 0原文

我有一个 Java 函数,如下所示:

private static long fieldPrime = 4294967291L; // 2^32-5
public final static long modMult(long a, long b) {
    long r = (a*b) % fieldPrime;        
    return r;
}

它将两个值相乘(保证在 0 和 2^32-5 之间),然后对一个大素数取模。

它适用于大多数数字,但有时 a*b 溢出,这会导致函数返回错误的值。不幸的是,Java 不支持无符号长整型(这本来可以解决问题)并且 BigInteger 太慢。我可以用其他方法解决这个问题吗?即,当我检测到溢出时,我可以以某种方式调整 r (在这种情况下 a*b < 0 总是意味着它已经溢出)。

I have a function Java which looks like this:

private static long fieldPrime = 4294967291L; // 2^32-5
public final static long modMult(long a, long b) {
    long r = (a*b) % fieldPrime;        
    return r;
}

It multiplies two values (which are guaranteed to be between 0 and 2^32-5) and then does modulo a large prime.

It works for most numbers but sometimes a*b overflows and this causes the function to return the wrong value. Unfortunately Java doesn't support unsigned longs (which would have solved the issue) and BigInteger is too slow. Can I solve this some other way? I.e. can I adjust r somehow when I detect overflow (in this case a*b < 0 always means it has overflowed).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

神回复 2024-12-18 04:15:20

这应该可行(如果 a 和 b 都在 0 和 fieldPrime-1 之间):

  private static long fieldPrime = 4294967291L; // 2^32-5
  private static long correctionFactor = fieldPrime+25; //fieldPrime + (2^64) mod fieldPrime 

  public final static long modMult(long a, long b) {
      long r = (a*b);
      if (r>=0)
      {
        return r % fieldPrime;
      }
      else
      {
        return ((r% fieldPrime)+correctionFactor)%fieldPrime;  
      }
  }

当发生溢出时,a*b 实际上是 a * b - 2^64,因此需要添加 (2^64 mod fieldPrime)。需要再加1个fieldPrime和1个%运算,使结果在0到fieldPrime-1范围内(否则可能为负数)。

(如果 fieldPrime>2^32,则不会以这种方式工作。)

编辑
else部分也可以改成:(

    return (fieldPrime-a)*(fieldPrime-b)%fieldPrime;

不知道哪个更快。)

This should work (if both a and b are between 0 and fieldPrime-1):

  private static long fieldPrime = 4294967291L; // 2^32-5
  private static long correctionFactor = fieldPrime+25; //fieldPrime + (2^64) mod fieldPrime 

  public final static long modMult(long a, long b) {
      long r = (a*b);
      if (r>=0)
      {
        return r % fieldPrime;
      }
      else
      {
        return ((r% fieldPrime)+correctionFactor)%fieldPrime;  
      }
  }

When overflow occurs a*b will actually be a * b - 2^64 so adding (2^64 mod fieldPrime) is what is needed. Adding one more fieldPrime and one more % operation is needed to make the result in range 0 to fieldPrime-1 (otherwise it may be negative).

(It won't work this way if fieldPrime>2^32.)

EDIT
The else part can also be changed to:

    return (fieldPrime-a)*(fieldPrime-b)%fieldPrime;

(I don't known which is faster.)

随波逐流 2024-12-18 04:15:20

一旦您知道自己再次处于安全范围内(取模后),您可以将 a、b 和 fieldPrime 转换为双精度并将结果转换回 long。然而,在某些情况下,双精度数可能会导致舍入错误。

除此之外,我想说 BigInteger 是你最好的选择,如果它太慢,也许你可以制作一些可以使用字节数组而不是长整型的东西,但我怀疑这会快得多。

You could convert a, b, and fieldPrime to doubles and cast the result back to a long once you know you're in a safe range again (after modulo). However it's possible that doubles could cause rounding errors in some cases.

Other than that I'd say BigInteger is your best bet, if it's too slow, maybe you could make something that would work with byte arrays instead of longs, but I doubt that would be much quicker.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文