¿Cómo puedo generar un número aleatorio dentro de un rango pero excluirlo?

¿Cómo puedo generar un número aleatorio dentro de un rango pero excluirlo, sin seguir generando y comprobando si el número generado es uno de los que quiero excluir?

Una posible solución sin regeneración, el azar cada vez es usar el siguiente algoritmo:

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

Este método se puede llamar con una referencia de matriz, por ejemplo

 int[] ex = { 2, 5, 6 }; val = getRandomWithExclusion(rnd, 1, 10, ex) 

o insertando directamente los números en la llamada:

 val = getRandomWithExclusion(rnd, 1, 10, 2, 5, 6) 

Genera un número aleatorio (int) entre el start y el end (ambos inclusive) y no le proporciona ningún número que esté incluido en la matriz exclude . Todos los demás números ocurren con la misma probabilidad. Tenga en cuenta que las siguientes restricciones deben ser exclude : exclude se ordena de forma ascendente y todos los números están dentro del rango provisto y todos ellos son mutuamente diferentes.

 /** * @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; } 

La idea es reducir el rango en el que se genera el número aleatorio a la diferencia entre el inicio y el conteo negativo de números dentro de ese rango que están excluidos.

Entonces obtienes una duración de rango que es idéntica al recuento de posibles números válidos. En otras palabras: eliminaste todos los agujeros del rango.

Después de generar el número aleatorio, debes volver a poner los “agujeros” en el rango. Esto se puede lograr incrementando el número generado siempre que haya números excluidos inferiores o iguales a los generados. Los números de exclusión inferiores son “agujeros” en el rango antes del número generado. Y el número generado se desplaza a la derecha para cada hoyo antes de ese número.

El mejor enfoque que puede seguir para aleatorizar números, excluyendo algunos, es seleccionar primero los números que desea y luego seleccionar aleatoriamente los números seleccionados. Por ejemplo, en pseudocódigo:

 List numbers; numbers.add(1); numbers.add(2); numbers.add(3); //You can do a "for" without adding the excluded numbers.. //Then, your randomizer could be... public Number getRandoNumber() { int index = Random.get(0, numbers.size()); return numbers.get(index); } 

Ahora, no necesita verificar si el “número generado” está permitido o no, porque no existe en absoluto.

Si no quiere que se repitan, puede hacer algo como:

 Collections.shuffle(numbers); public Number getRandomNotRepeat() { if(numbers.size() == 0) throw new RuntimeException("No more numbers"); Number n = numbers.get(0); numbers.removeFirst(); return n; } 

¡Esto es todo pseudo código, no copie y pegue!