如何在一个范围内生成一个随机数但排除一些?

AAa*_*Aaa 34 java random

如何生成一个范围内的随机数但排除一些,而不继续生成并检查生成的数字是否是我要排除的数字之一?

How*_*ard 44

每次随机无需再生的一种可能解决方案是使用以下算法:

public int getRandomWithExclusion(Random rnd, int start, int end, int... exclude) {
    int random = start + rnd.nextInt(end - start + 1 - exclude.length);
    for (int ex : exclude) {
        if (random < ex) {
            break;
        }
        random++;
    }
    return random;
}
Run Code Online (Sandbox Code Playgroud)

可以使用数组引用调用此方法,例如

int[] ex = { 2, 5, 6 };
val = getRandomWithExclusion(rnd, 1, 10, ex)
Run Code Online (Sandbox Code Playgroud)

或直接将号码插入电话:

val = getRandomWithExclusion(rnd, 1, 10, 2, 5, 6)
Run Code Online (Sandbox Code Playgroud)

它在(start和)之间生成一个随机数(int),并且end不会为您提供数组中包含的任何数字exclude.所有其他数字的概率相等.请注意,以下约束必须保持:exclude按升序排序,所有数字都在提供的范围内,并且所有数字都是相互不同的.

  • 我想评论一下你的回答。您的解决方案仅在您订购 ex 数组时才有效,否则将无法正常工作 (3认同)
  • 这种方法让我着迷。我对此做了一些基准测试:`do{ draw=start+rnd.nextInt(end);} while (Arrays.binarySearch(ex, draw) &gt;=0);` 发现有时搜索数组更快。随着排除与池的比率接近 1(例如 11 个池中的 10 个排除数字),搜索数组变得非常慢(1 亿次迭代需要 56 秒,而使用上述方法需要 3.5 秒)但是如果将池扩展到 1000,例如搜索阵列更快(4.1s vs 4.7s)。再一次,这样做是 1 亿次。上述单次大约需要 10000 纳秒,而搜索则需要 20000 纳秒。 (2认同)

Fab*_*ney 16

/**
 * @param start start of range (inclusive)
 * @param end end of range (exclusive)
 * @param excludes numbers to exclude (= numbers you do not want)
 * @return the random number within start-end but not one of excludes
 */
public static int nextIntInRangeButExclude(int start, int end, int... excludes){
    int rangeLength = end - start - excludes.length;
    int randomInt = RANDOM.nextInt(rangeLength) + start;

    for(int i = 0; i < excludes.length; i++) {
        if(excludes[i] > randomInt) {
            return randomInt;
        }

        randomInt++;
    }

    return randomInt;
}
Run Code Online (Sandbox Code Playgroud)

该想法是将产生随机数的范围减小到开始和结束之间的差减去该范围内被排除的数字的数量.

因此,您获得的范围长度与可能的有效数字的数量相同.换句话说:你已经从范围中移除了所有洞.

生成随机数后,您需要将"孔"放回范围内.只要排除的数字低于或等于生成的数字,就可以通过递增生成的数字来实现这一点.较低的排除数是在生成的数字之前的范围内的"空洞".并且在该数字之前为每个孔移动生成的数字.