Java inverse modulo 2**64

☆樱花仙子☆ 提交于 2019-12-07 06:22:56

问题


Given an odd long x, I'm looking for long y such that their product modulo 2**64 (i.e., using the normal overflowing arithmetic) equals to 1. To make clear what I mean: This could be computed in a few thousand year this way:

for (long y=1; ; y+=2) {
    if (x*y == 1) return y;
}

I know that this can be solved quickly using the extended Euclidean algorithm, but it requires the ability to represent all the involved numbers (ranging up to 2**64, so even unsigned arithmetic wouldn't help). Using BigInteger would surely help, but I wonder if there's a simpler way, possibly using the extended Euclidean algorithm implemented for positive longs.


回答1:


Here's one way of doing it. This uses the extended Euclidean algorithm to find an inverse of abs(x) modulo 262, and at the end it 'extends' the answer up to an inverse modulo 264 and applies a sign change if necessary:

public static long longInverse(long x) {

    if (x % 2 == 0) { throw new RuntimeException("must be odd"); }

    long power = 1L << 62;

    long a = Math.abs(x);
    long b = power;
    long sign = (x < 0) ? -1 : 1;

    long c1 = 1;
    long d1 = 0;
    long c2 = 0;
    long d2 = 1;

    // Loop invariants:
    // c1 * abs(x) + d1 * 2^62 = a
    // c2 * abs(x) + d2 * 2^62 = b 

    while (b > 0) {
        long q = a / b;
        long r = a % b;
        // r = a - qb.

        long c3 = c1 - q*c2;
        long d3 = d1 - q*d2;

        // Now c3 * abs(x) + d3 * 2^62 = r, with 0 <= r < b.

        c1 = c2;
        d1 = d2;
        c2 = c3;
        d2 = d3;
        a = b;
        b = r;
    }

    if (a != 1) { throw new RuntimeException("gcd not 1 !"); }

    // Extend from modulo 2^62 to modulo 2^64, and incorporate sign change
    // if necessary.
    for (int i = 0; i < 4; ++i) {
        long possinv = sign * (c1 + (i * power));
        if (possinv * x == 1L) { return possinv; }
    }

    throw new RuntimeException("failed");
}

I found it easier to work with 262 than 263, mainly because it avoids problems with negative numbers: 263 as a Java long is negative.




回答2:


In the meantime I've recalled/reinvented a very simple solution:

public static int inverseOf(int x) {
    Preconditions.checkArgument((x&1)!=0, "Only odd numbers have an inverse, got " + x);
    int y = 1;
    for (int mask=2; mask!=0; mask<<=1) {
        final int product = x * y;
        final int delta = product & mask;
        y |= delta;
    }
    return y;
}

It works because of two things:

  • in each iteration if the corresponding bit of product is 1, then it's wrong, and the only way to fix is is by changing the corresponding bit of y
  • no bit of y influences any less significant bit of product, so no previous work gets undone

I started with int since for long it must work too, and for int I could run an exhaustive test.

Another idea: there must a a number n>0 such that x**n == 1, and therefore y == x**(n-1). This should probably be faster, I just can't recall enough math to compute n.



来源:https://stackoverflow.com/questions/11702215/java-inverse-modulo-264

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!