如何在特定范围内生成随机int值?
以下方法存在与整数溢出相关的错误:
randomNum = minimum + (int)(Math.random() * maximum);
// Bug: `randomNum` can be bigger than `maximum`.
Random rn = new Random();
int n = maximum - minimum + 1;
int i = rn.nextInt() % n;
randomNum = minimum + i;
// Bug: `randomNum` can be smaller than `minimum`.
我将简单地说明问题提供的解决方案有什么问题,以及错误的原因。
解决方案1:
randomNum = minimum + (int)(Math.random()*maximum);
问题:randomNum分配的值大于最大值。
解释:假设我们的最小值是5,而你的最大值是10。Math.random()中任何大于0.6的值都将使表达式的计算结果为6或更大,加上5将使其大于10(最大值)。问题是你将随机数乘以最大值(这会产生一个几乎和最大值一样大的数字),然后再加上最小值。除非最小值是1,否则它是不正确的。如其他答案所述,您必须切换到
randomNum = minimum + (int)(Math.random()*(maximum-minimum+1))
+1是因为Math.random()永远不会返回1.0。
解决方案2:
Random rn = new Random();
int n = maximum - minimum + 1;
int i = rn.nextInt() % n;
randomNum = minimum + i;
这里的问题是,如果第一项小于0,“%”可能会返回负数。由于rn.nextInt()以约50%的概率返回负值,因此也不会得到预期的结果。
然而,这几乎是完美的。您只需进一步查看Javadoc,nextInt(int n)。使用该方法
Random rn = new Random();
int n = maximum - minimum + 1;
int i = rn.nextInt(n);
randomNum = minimum + i;
也将返回所需的结果。
这里有一个有用的类,可以在包含/排除边界的任意组合范围内生成随机整数:
import java.util.Random;
public class RandomRange extends Random {
public int nextIncInc(int min, int max) {
return nextInt(max - min + 1) + min;
}
public int nextExcInc(int min, int max) {
return nextInt(max - min) + 1 + min;
}
public int nextExcExc(int min, int max) {
return nextInt(max - min - 1) + 1 + min;
}
public int nextIncExc(int min, int max) {
return nextInt(max - min) + min;
}
}
请原谅我过于挑剔,但大多数人建议的解决方案,即min+rng.nextInt(max-min+1),似乎很危险,因为:
rng.nextInt(n)无法达到整数.MAX_VALUE。当min为负值时,(max-min)可能会导致溢出。
万无一失的解决方案将为[Integer.min_VALUE,Integer.max_VALUE]内的任何min<=max返回正确的结果。请考虑以下简单的实现:
int nextIntInRange(int min, int max, Random rng) {
if (min > max) {
throw new IllegalArgumentException("Cannot draw random int from invalid range [" + min + ", " + max + "].");
}
int diff = max - min;
if (diff >= 0 && diff != Integer.MAX_VALUE) {
return (min + rng.nextInt(diff + 1));
}
int i;
do {
i = rng.nextInt();
} while (i < min || i > max);
return i;
}
尽管效率低下,但请注意while循环中成功的概率始终为50%或更高。