web-dev-qa-db-fra.com

Comment puis-je générer un nombre aléatoire dans une plage mais en exclure certains?

Comment puis-je générer un nombre aléatoire dans une plage mais en exclure certains, sans continuer à générer et à vérifier si le nombre généré est l'un de ceux que je souhaite exclure?

33
AAaa

Une solution possible sans régénération aléatoire consiste à utiliser l’algorithme suivant:

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

Cette méthode peut être appelée avec une référence de tableau, par exemple.

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

ou en insérant directement les numéros dans l'appel:

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

Il génère un nombre aléatoire (int) compris entre start et end (les deux inclus) et ne vous donne aucun nombre contenu dans le tableau exclude. Tous les autres nombres apparaissent avec une probabilité égale. Notez que les contraintes suivantes doivent être respectées: exclude est trié par ordre croissant et tous les nombres sont compris dans la plage fournie et sont tous mutuellement différents.

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

L'idée est de réduire la plage dans laquelle le nombre aléatoire est généré à la différence entre le début et la fin moins le nombre de nombres dans cette plage qui sont exclus.

Ainsi, vous obtenez une longueur de plage identique au nombre de nombres valides possibles. En d'autres termes: vous avez supprimé tous les trous de la plage.

Après avoir généré le nombre aléatoire, vous devez replacer les "trous" dans la plage. Cela peut être réalisé en incrémentant le nombre généré tant qu'il existe des nombres exclus inférieurs ou égaux à ceux générés. Les numéros d'exclusion les plus bas sont des "trous" dans la plage précédant le nombre généré. Et le nombre généré est décalé à droite pour chaque trou avant ce nombre.

15
Fabian Barney

La meilleure approche que vous puissiez suivre pour sélectionner des nombres au hasard, à l’exclusion de certains, consiste à sélectionner les numéros que vous souhaitez d’abord, puis à sélectionner de manière aléatoire les numéros sélectionnés. Par exemple, en pseudocode:

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

Maintenant, vous n'avez pas besoin de vérifier si le "numéro généré" est autorisé ou non, car il n'existe pas du tout.

Si vous ne voulez pas qu'ils se répètent, vous pouvez faire quelque chose comme:

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

Ceci est tout pseudo-code, ne pas copier et coller! 

3
anizzomc

Je pense que la question supplémentaire est: quels sont les chiffres que vous voulez obtenir? Représentent-ils une sorte de plage ou sont-ils totalement aléatoires ?

Si vous souhaitez ignorer une plage de nombres, vous pouvez générer vos nombres aléatoires à partir de quelques jeux représentant uniquement des nombres valides:

Rand(1,9);
Rand(15,19);
Rand(22,26);

de cette façon, vous êtes sûr de ne jamais sélectionner les exclus: <0,10,11,12,13,14,20,21,> 27

Ensuite, lorsque vous aurez vos 3 numéros, vous pourrez à nouveau sélectionner l’un d’eux au hasard.

Si les numéros exclus sont partout, je crains que vous ne deviez le vérifier à chaque fois par rapport à une sorte de collection de numéros exclus.

2
rochal

Créez une carte qui prend la sortie d'une fonction aléatoire sans restriction de plage et mappez-la sur la plage souhaitée avec des restrictions.

Par exemple, si je veux un int aléatoire de 1-10 mais jamais 7, je pourrais faire quelque chose comme ceci:

int i = Rand(1, 9);
if i>=7
  i++;
return i;

Tant que vous vous assurez que votre mappage est 1: 1, vous pouvez éviter de fausser le caractère aléatoire de votre fonction Rand.

1
Ax.

les numéros d'exclusion doivent être avec le paramètre in range 

private int GiveMeANumber(int range,int... exclude)
{

    Set<Integer> integers=new HashSet<>();
    int count=range;

    for(int i=0;i<count;i++)
        integers.add(i);

    integers.removeAll(Arrays.asList(exclude));


    int index = new Random().nextInt(range - exclude.length);

    count=0;

    for (int value:integers){
        if(count==index)
            return value;

        count++;
    }


    return 0;
}
0
Nishi Sahlot

En fonction de la taille de la liste de nombres aléatoires que vous excluez, je générerais simplement vos nombres et vérifierais si elle se trouve dans le tableau des nombres exclus - le cas échéant, supprimez-la simplement. Je sais que vous ne voulez pas vérifier chaque fois, mais je ne peux pas penser à une autre façon en plus de spécifier explicitement les plages et si vous avez plus de 5 chiffres que vous excluez, cela pourrait être un peu pire.

0
LainIwakura

Quelque chose qui pourrait fonctionner et qui s'applique à la fois aux nombres entiers et doubles pourrait ressembler à: 

public int getRandomNumberWithExclusion( int start, int end )
{
  Random r = new Random();
  int result = -1;

  do
  {
      result = start + r.nextInt( end - start );
  }//do
  while( !isAllowed( result ) );

  return result;

}//met

private boolean isAllowed( int number )
{
   //your test for restricted values here
}//met

Cordialement, Stéphane

0
Snicolas