Java Math.abs随机数与nextInt()的区别

3
在Java中创建一个随机数生成器 - 通过观察num1和num2,什么情况下我会使用这两种方法之一来创建一个介于1到8之间的随机数?其中一种方法是否比另一种更有效,还有其他好处吗?
import java.util.*;

public class Chpt3 {
  public static void main(String[] args) {
    Random rand = new Random();
    int num1 = Math.abs(rand.nextInt()) % 8 + 1;
    int num2 = rand.nextInt(8) + 1;
    System.out.println(num1);
    System.out.println(num2);

  }
}

由于您想要一个范围在[1,9]的整数,因此您应该使用rand.nextInt,因为它更好地描述了您的意图。除非您知道其他情况,否则更具体函数的内部实现往往比将它们组合成现有函数更好。话虽如此,Java是一种编译语言,并利用静态分析使得这些决策几乎无关紧要,将两个模式Math.abs(Math.random)和random.nextInt规范化为几乎相同的表达式。 - Dmytro
3个回答

6

nextInt(n)返回0n-1之间的随机整数。

因此,要得到1到8之间的随机整数:

int num2 = rand.nextInt(8) + 1;

这意味着你需要第二个方法。
更新: Math.abs(Integer.MIN_VALUE) 返回负数。
根据 SO 上这个答案

Integer.MIN_VALUE 是 -2147483648,但 32 位整数可以包含的最大值是 +2147483647。试图用 32 位 int 表示 +2147483648 将有效地“滚动”到 -2147483648。这是因为在使用有符号整数时,+2147483648-2147483648 的二进制补码表示是相同的。然而这不是问题,因为 +2147483648 被认为是超出范围的。

如果您想更深入地了解此问题,可以查看维基百科关于二进制补码的文章。

第一个方法只包含一个罕见的边缘情况。这就是为什么最好使用第二种方法,因为它是安全的。

你能否解释一下为什么不选择第一个选项,它似乎可以做同样的事情? - Peter Lawrey
2
他没有说Math.abs(Integer.MIN_VALUE)返回负数是一个错误。他说Math.abs(rand.nextInt()) % 8 + 1有一个错误,因为Math.abs(Integer.MIN_VALUE)可以(正确地)返回负数。 - shmosel
@shmosel 我错了。我不是以英语为母语的人。 - user6528991

5

Math.abs(rand.nextInt() % 8) + 1;

这段代码存在一个微妙的 bug。Math.abs(Integer.MIN_VALUE) 返回 MIN_VALUE,是一个负数。更简单的解决方法是

(rand.nextInt() & 7) + 1;

这将始终为非负数,并且速度稍快。

rand.nextInt(8) + 1

这种方法不仅更快,更重要的是更清晰地表达了您想要实现的目标。因为后一种原因,而不仅仅是速度,它是更好的选择。

注意:如果您真的想这样做,可以使用 abs。但是,这不如 nextInt 调用干净。

Math.abs(rand.nextInt() % 8) + 1;

2
这里是问题:
int num1 = Math.abs(rand.nextInt()) % 8 + 1;

这意味着你会首先从“使用(大约)相等概率生成所有2^32个可能的int值”中选择一个数字,然后将其余数除以8。由于我们使用了Math.abs(),问题在于它可能返回一个负数。

 int num2 = rand.nextInt(8) + 1;

然而,这个版本不会返回负数,并且将返回0到8之间的数字。

两种代码都能实现你想要做的事情。但是,第二行代码对于内存来说会更好,因为在第一个版本中,最坏的情况是需要记住2^32个数字,但是在第二个版本中不需要。另外一件事是,因为我们需要使用更少的方法和更少的计算,总体上第二种方法会更快。总之,两种方法都可以工作,但第二个版本是最好的版本,因为它占用更少的内存并且速度更快。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接