【问题标题】:Java - Is there a method for Euclidean or floored moduloJava - 是否有欧几里得或底模的方法
【发布时间】:2013-01-24 01:31:57
【问题描述】:

Java 模运算符% 基于截断除法(请参阅Wikipedia: Modulo operation)。

  • 5%3 产生2(注意5/3 产生1
  • 5%(-3) 产生2(注意5/(-3) 产生-1
  • (-5)%3 产生-2(注意(-5)/3 产生-1
  • (-5)%(-3) 产生-2(注意(-5)/(-3) 产生1

在计算科学中,给定两个整数 ann > 0,有时在 [a,n[ 中获取唯一整数 r 很有用,它与 a 模 @987654346 一致@。

问题

在 Java 中是否有一个高效的通用运算符/方法尊重这个模数规范?

这是为了避免在每个需要它的项目中重写它...

杂项

我在 stackoverflow 上发现了很多关于这个问题的问题,其中大多数混淆了不同的模实现。如果您只是对负数的模运算的结果感到困扰,下面是一些基于 Java % 运算符的实现,可能有用。

常见技巧

由于我们几乎不使用负除数,因此此实现在 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

地板模数

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

【问题讨论】:

  • 您可能已经检查过了,但是您可以使用Math.floor() (JavaDocs) 轻松地对值(最接近的整数)进行下限
  • @Killrawr Math.floor() 比上述任何解决方案都差。
  • @Killrawr a - n * (int)Math.floor((double)a/n); 在数学上对于底模是正确的,但效率不高,也不通用。
  • @boumbh 你想要哪种行为? (-5)magicmod(-3) 应该给什么? -22?
  • 哈哈我还没有尝试过问题中的任何解决方案(以寻找改进)。但是无论如何感谢UmNyobe(+1代表)的澄清。通常我在数学库中找到的大多数函数都很棒,尽管像 cos/sin/tan/pow/sqrt/log/exp。但我并没有真正用地板/天花板功能编写任何东西(对不起,我帮不上什么忙)。

标签: java operators modulo modulus negative-number


【解决方案1】:

这段代码怎么样

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);
}

【讨论】:

    【解决方案2】:
    +----+----+------------+----------+------------+----- ------+---------+------------+ | x mod y |商'q' |余数'r' | | x |是 |截断 |地板 |欧几里得 |截断 |地板 |欧几里得 | +----+----+------------+----------+------------+----- ------+---------+------------+ | 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 8 起,您可以使用 java.lang.Math 中的方法。请参阅 floorDivfloorMod

    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;
    }
    

    【讨论】:

    • 您好 Vlastimil,感谢您提供有关 Java 8 的好消息。您的实现有一些问题。当我尝试版本 a) 和版本 b) 时,euclideanMod(-4, -2) 返回 2 而不是 0,对于版本 c),euclideanModA(5, -3) 返回 2 而不是 1。也许我做错了什么,你能确认一下吗?
    • 我阅读了您的评论,我错了,我需要时间来纠正错误。您在实现 a) 和 b) 时仍然可能有错误?
    • 不,你是对的。事实上,我以草稿的形式发送了答案——我没想到有人会在下周末之前对其进行测试。我想要“无 IF”替代您的解决方案 - 说我正在考虑完全删除 a) 和 b)。
    • @boumbh,我放弃了没有 IF 的想法,只是用 Java 8 源代码启发了自己。我会使用基于截断除法的欧几里得模数,因为那是 Java 中的默认除法。然而,绝对模算法更容易记住。
    • 嗨 Vlastimil,我终于测试了它。我很惊讶截断的版本如此之快。我认为像int r = x % y; return r &gt;= 0?r:r + Math.abs(y); 这样的东西会更快,因为只有一个除法,没有乘法......你的截断版本是我目前发现的最快的。
    猜你喜欢
    • 2014-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    相关资源
    最近更新 更多