Java. Есть ли метод для евклидовой или напольной модуляции

Java modulo operator % основан на усеченном делении (см. Википедия: операция Modulo).

  • 5%3 создает 2 (обратите внимание, что 5/3 создает 1)
  • 5%(-3) создает 2 (обратите внимание, что 5/(-3) создает -1)
  • (-5)%3 создает -2 (обратите внимание, что (-5)/3 создает -1)
  • (-5)%(-3) создает -2 (обратите внимание, что (-5)/(-3) производит 1)

В вычислительной науке, учитывая два целых числа a и n, n > 0, иногда полезно получить единственное целое число r внутри [a,n[, которое соответствует a по модулю n.

Вопрос

Существует ли в Java эффективный общий оператор/метод, который соблюдает эту спецификацию по модулю?

Это нужно, чтобы не переписывать его в каждом проекте, где это необходимо...

Разное

Я нашел много вопросов о stackoverflow об этой проблеме, большинство из которых путают различные реализации по модулю. Если вы просто обеспокоены результатами работы модуля по отрицательным числам, ниже приведены некоторые реализации на основе Java % operator, которые могут быть полезны.

Общий хак

Поскольку мы вряд ли используем отрицательный делитель, эта реализация возвращает евклидово или поэтапное по модулю, когда n > 0.

static int mod(int a, int n){    
  return a<0 ? (a%n + n)%n : a%n;
}
  • mod( 5, 3) создает 2
  • mod(-5, 3) создает 1

Евклидова по модулю

static int euclideanModulo(int a, int n){
  return n<0 ? euclideanModulo(a, -n) : mod(a, n);
}
  • euclideanModulo( 5, 3) создает 2
  • euclideanModulo(-5, 3) создает 1
  • euclideanModulo( 5,-3) создает 2
  • euclideanModulo(-5,-3) создает 1

Floored modulo

static int flooredModulo(int a, int n){
  return n<0 ? -flooredModulo(-a, -n) : mod(a, n);
}
  • flooredModulo( 5, 3) создает 2
  • flooredModulo(-5, 3) создает 1
  • flooredModulo( 5,-3) создает -1
  • flooredModulo(-5,-3) создает -2

Ответ 1

+----+----+-----------+---------+-----------+-----------+---------+-----------+
| x mod y |           quotient 'q'          |          remainder 'r'          |
| x  | y  | truncated | floored | Euclidean | truncated | floored | Euclidean |
+----+----+-----------+---------+-----------+-----------+---------+-----------+
|  5 |  3 |         1 |       1 |         1 |         2 |       2 |         2 |
| -5 |  3 |        -1 |      -2 |        -2 |        -2 |       1 |         1 |
|  5 | -3 |        -1 |      -2 |        -1 |         2 |      -1 |         2 |
| -5 | -3 |         1 |       1 |         2 |        -2 |      -2 |         1 |
+----+----+-----------+---------+-----------+-----------+---------+-----------+

Любое из них удовлетворяет хотя бы x = yq + r.

Усеченное деление и по модулю

static int truncatedDiv(int x, int y) {    
    return x / y;
}

static int truncatedMod(int x, int y) {    
    return x % y;
}

Плавающее деление и по модулю

Вы можете использовать методы в java.lang.Math с Java 8. См. floorDiv и floorMod.

static int floorDiv(int x, int y) {    
    return Math.floorDiv(x, y);
}

static int floorMod(int x, int y) {    
    return Math.floorMod(x, y);
}

Евклидово деление и по модулю

a) на основе усеченного деления

import static java.lang.Math.*;

static int euclideanDiv(int x, int y) {
    int r = x / y;
    // if the divident is negative and modulo not zero, round down for positive divisor, otherwise round up
    if (x < 0 && r * y != x) {
        r -= signum(y);
    }
    return r;
}

static int euclideanMod(int x, int y) {
    int r = x - euclideanDiv(x, y) * y;
    return r;
}

b), основанный на разделении пола

import static java.lang.Math.*;

static int euclideanDiv(int x, int y) {
    int r = floorDiv(x, y);
    // if the divisor is negative and modulo not zero, round up
    if (y < 0 && r * y != x) {
        r++;
    }
    return r;
}

static int euclideanMod(int x, int y) {
    int r = x - euclideanDiv(x, y) * y;
    return r;
}

c), основанный на абсолютном модуле

import static java.lang.Math.*;

static int euclideanMod(int x, int y) {
    int r = abs(x) % abs(y);
    // apply the sign of divident and make sure the remainder is positive number
    r *= signum(x);
    r = (r + abs(y)) % abs(y);
    return r;
}

Ответ 2

как насчет этого кода

public static int gcd(int p, int q) {
    if(count == 0) 
        System.out.print("Gcd for " + p + " and " + q);
    if (q == 0) {
           System.out.println(" returns " + p + " after " + count + " iterations");
        return p;
    }
    count++;
    return gcd(q, p % q);
}
public static void main(String[] args) {
    count = 0;
    gcd(4, 16);
    count = 0;
    gcd(4, 16);
    count = 0;
    gcd(16, 4);
    count = 0;
    gcd(15, 60);
    count = 0;
    gcd(15, 65);
    count = 0;
    gcd(1052, 52);
}