Générer des nombres aléatoires uniques en Java


88

J'essaie d'obtenir des nombres aléatoires entre 0 et 100. Mais je veux qu'ils soient uniques et non répétés dans une séquence. Par exemple, si j'ai 5 nombres, ils devraient être 82,12,53,64,32 et non 82,12,53,12,32 J'ai utilisé ceci, mais cela génère les mêmes nombres dans une séquence.

Random rand = new Random();
selected = rand.nextInt(100);

5
Vous pouvez créer une permutation aléatoire de la plage 1..100(il existe des algorithmes célèbres pour cela), mais arrêtez-vous après avoir déterminé les premiers néléments.
Kerrek SB



Réponses:


145
  • Ajoutez chaque nombre de la plage de manière séquentielle dans une structure de liste .
  • Mélangez- le.
  • Prenez le premier «n».

Voici une implémentation simple. Cela imprimera 3 nombres aléatoires uniques de la gamme 1-10.

import java.util.ArrayList;
import java.util.Collections;

public class UniqueRandomNumbers {

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int i=1; i<11; i++) {
            list.add(new Integer(i));
        }
        Collections.shuffle(list);
        for (int i=0; i<3; i++) {
            System.out.println(list.get(i));
        }
    }
}

La première partie du correctif avec l'approche d'origine, comme Mark Byers l'a souligné dans une réponse maintenant supprimée, consiste à n'utiliser qu'une seule Randominstance.

C'est ce qui fait que les chiffres sont identiques. Une Randominstance est prédéfinie par l'heure actuelle en millisecondes. Pour une valeur de départ particulière , l'instance «aléatoire» renverra exactement la même séquence de nombres pseudo aléatoires .

Notez que le public Integer​(int value)constructeur est deprecateddepuis Java 9.

La première boucle for peut simplement être changée en:

for (int i = 1; i < 11; i++) {
  list.add(i);
}

3
+1 pour signaler une seule instance aléatoire et répondre à la question. :)
Mark Byers

Vous n'êtes pas obligé de mélanger toute la plage. Si vous voulez n nombres uniques, il vous suffit de mélanger la première position n en utilisant un mélange Fisher-Yates. Cela peut aider avec une grande liste et un petit n.
rossum

59

Avec Java 8+, vous pouvez utiliser la intsméthode de Randompour obtenir IntStreamensuite des valeurs aléatoires distinctet limitréduire le flux à un certain nombre de valeurs aléatoires uniques.

ThreadLocalRandom.current().ints(0, 100).distinct().limit(5).forEach(System.out::println);

Randoma également des méthodes qui créent des LongStreams et des DoubleStreams si vous en avez besoin à la place.

Si vous voulez que tous (ou une grande quantité) des nombres dans une plage dans un ordre aléatoire, il peut être plus efficace d'ajouter tous les nombres à une liste, de la mélanger et de prendre le premier n car l'exemple ci-dessus est actuellement implémenté en générant des nombres aléatoires dans la plage demandée et en les passant à travers un ensemble (similaire à la réponse de Rob Kielty ), ce qui peut nécessiter de générer beaucoup plus que le montant passé à la limite car la probabilité de générer un nouveau nombre unique diminue avec chacun trouvé. Voici un exemple de l'autre manière:

List<Integer> range = IntStream.range(0, 100).boxed()
        .collect(Collectors.toCollection(ArrayList::new));
Collections.shuffle(range);
range.subList(0, 99).forEach(System.out::println);

J'en avais besoin pour du code que je compare, et Arrays#setAll()c'est un peu plus rapide qu'un flux. Donc: `Integer [] indices = new Integer [n]; Arrays.setAll (indices, i -> i); Collections.shuffle (Arrays.asList (indices)); return Arrays.stream (indices) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar

18
  1. Créez un tableau de 100 nombres, puis randomisez leur ordre.
  2. Concevez un générateur de nombres pseudo-aléatoires qui a une plage de 100.
  3. Créez un tableau booléen de 100 éléments, puis définissez un élément sur true lorsque vous choisissez ce nombre. Lorsque vous choisissez le numéro suivant, vérifiez le tableau et réessayez si l'élément du tableau est défini. (Vous pouvez créer un tableau booléen facile à effacer avec un tableau longoù vous déplacez et masquez pour accéder à des bits individuels.)

2
+1 pour l'approche alternative; pick()est un exemple.
trashgod

1
Au lieu d'utiliser un tableau booléen, vous pouvez utiliser a HashSet, où vous stockez les nombres que vous avez déjà générés et utilisez containspour tester si vous avez déjà généré ce nombre. Le HashSetsera probablement légèrement plus lent qu'un tableau booléen, mais prendra moins de mémoire.
Rory O'Kane

1
@ RoryO'Kane - Je suis presque sûr que le tableau booléen prendrait moins d'espace, s'il était implémenté comme un tableau de long [2]. Pas question de créer un HashSet aussi petit.
Hot Licks

La dernière approche est un peu moche car elle n'aurait pas un nombre bien défini d'étapes pour générer la séquence entière. Vous n'avez pas non plus besoin de réinventer la roue - BitSet .
Pavel Horal

16

Utilisez Collections.shuffle()sur les 100 numéros et sélectionnez les cinq premiers, comme indiqué ici .


13

Je pense que cette méthode mérite d'être mentionnée.

   private static final Random RANDOM = new Random();    
   /**
     * Pick n numbers between 0 (inclusive) and k (inclusive)
     * While there are very deterministic ways to do this,
     * for large k and small n, this could be easier than creating
     * an large array and sorting, i.e. k = 10,000
     */
    public Set<Integer> pickRandom(int n, int k) {
        final Set<Integer> picked = new HashSet<>();
        while (picked.size() < n) {
            picked.add(RANDOM.nextInt(k + 1));
        }
        return picked;
    }

9

J'ai retravaillé la réponse d'Anand pour utiliser non seulement les propriétés uniques d'un ensemble, mais aussi utiliser le booléen false renvoyé par le set.add()lorsqu'un ajout à l'ensemble échoue.

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class randomUniqueNumberGenerator {

    public static final int SET_SIZE_REQUIRED = 10;
    public static final int NUMBER_RANGE = 100;

    public static void main(String[] args) {
        Random random = new Random();

        Set set = new HashSet<Integer>(SET_SIZE_REQUIRED);

        while(set.size()< SET_SIZE_REQUIRED) {
            while (set.add(random.nextInt(NUMBER_RANGE)) != true)
                ;
        }
        assert set.size() == SET_SIZE_REQUIRED;
        System.out.println(set);
    }
}

1
Bonne idée. Une note importante cependant - si SET_SIZE_REQUIREDest assez grand (disons, plus NUMBER_RANGE / 2
qu'alors,

5

J'ai fait ça comme ça.

    Random random = new Random();
    ArrayList<Integer> arrayList = new ArrayList<Integer>();

    while (arrayList.size() < 6) { // how many numbers u need - it will 6
        int a = random.nextInt(49)+1; // this will give numbers between 1 and 50.

        if (!arrayList.contains(a)) {
            arrayList.add(a);
        }
    }

4

Cela fonctionnera pour générer des nombres aléatoires uniques ................

import java.util.HashSet;
import java.util.Random;

public class RandomExample {

    public static void main(String[] args) {
        Random rand = new Random();
        int e;
        int i;
        int g = 10;
        HashSet<Integer> randomNumbers = new HashSet<Integer>();

        for (i = 0; i < g; i++) {
            e = rand.nextInt(20);
            randomNumbers.add(e);
            if (randomNumbers.size() <= 10) {
                if (randomNumbers.size() == 10) {
                    g = 10;
                }
                g++;
                randomNumbers.add(e);
            }
        }
        System.out.println("Ten Unique random numbers from 1 to 20 are  : " + randomNumbers);
    }
}

3

Une manière intelligente de faire ceci est d'utiliser les exposants d'un élément primitif dans le module.

Par exemple, 2 est un mod racine primitif 101, ce qui signifie que les puissances de 2 mod 101 vous donnent une séquence non répétitive qui voit chaque nombre de 1 à 100 inclus:

2^0 mod 101 = 1
2^1 mod 101 = 2
2^2 mod 101 = 4
...
2^50 mod 101 = 100
2^51 mod 101 = 99
2^52 mod 101 = 97
...
2^100 mod 101 = 1

Dans le code Java, vous écririez:

void randInts() {
int num=1;
for (int ii=0; ii<101; ii++) {
    System.out.println(num);
    num= (num*2) % 101;
    }
}

Trouver une racine primitive pour un module spécifique peut être délicat, mais la fonction "primroot" de Maple le fera pour vous.


C'est intéressant, mais comment pourrions-nous nous assurer que la séquence générée est aléatoire? Cela ne semble pas être le cas. Il semble très déterministe d'avoir 1,2,4,8,16, ... au début d'une séquence.
h4nek

Ce n'est pas aléatoire ... c'est pseudo-aléatoire. Personne ne sait comment générer des nombres vraiment aléatoires. Si vous n'aimez pas le modèle initial, vous pouvez utiliser une base plus large comme racine primitive.
AT - étudiant

Un pseudo-aléatoire serait bien. Mais ici, pour une "plage" donnée, la quantité de racines primitives et donc de séquences uniques est limitée, en particulier pour les plages plus petites. Il semble donc y avoir un problème avec le motif, par exemple ayant toujours une sous-séquence de puissances de la racine. Et ne pas obtenir une séquence (probablement) très différente sur plusieurs courses, à moins que nous n'appliquions plus de manigances. Je suppose que cela dépend du cas d'utilisation. Changer la base est de toute façon une belle mise à niveau, même si cela ne fait que "changer" le motif.
h4nek

2

Je suis venu ici d'une autre question, qui a été dupliquée de cette question ( Générer un nombre aléatoire unique en java )

  1. Stockez de 1 à 100 numéros dans un tableau.

  2. Génère un nombre aléatoire entre 1 et 100 comme position et renvoie le tableau [position-1] pour obtenir la valeur

  3. Une fois que vous utilisez un nombre dans le tableau, marquez la valeur comme -1 (pas besoin de maintenir un autre tableau pour vérifier si ce nombre est déjà utilisé)

  4. Si la valeur du tableau est -1, récupérez à nouveau le nombre aléatoire pour récupérer le nouvel emplacement du tableau.


2

J'ai une solution facile à ce problème, avec cela, nous pouvons facilement générer un nombre n de nombres aléatoires uniques, c'est juste logique que tout le monde peut l'utiliser dans n'importe quelle langue.

for(int i=0;i<4;i++)
        {
            rn[i]= GenerateRandomNumber();
            for (int j=0;j<i;j++)
            {
                if (rn[i] == rn[j])
                {
                    i--;
                }
            }
        }

vous pouvez optimiser en faisant un break;après lei—;
Jan il y a

0

essayez ça

public class RandomValueGenerator {
    /**
     * 
     */
    private volatile List<Double> previousGenValues = new ArrayList<Double>();

    public void init() {
        previousGenValues.add(Double.valueOf(0));
    }

    public String getNextValue() {
        Random random = new Random();
        double nextValue=0;
        while(previousGenValues.contains(Double.valueOf(nextValue))) {
            nextValue = random.nextDouble();
        }
        previousGenValues.add(Double.valueOf(nextValue));
        return String.valueOf(nextValue);
    }
}

0

Ce n'est pas très différent des autres réponses, mais je voulais le tableau d'entiers à la fin:

    Integer[] indices = new Integer[n];
    Arrays.setAll(indices, i -> i);
    Collections.shuffle(Arrays.asList(indices));
    return Arrays.stream(indices).mapToInt(Integer::intValue).toArray();

0

vous pouvez utiliser un tableau booléen pour remplir la valeur true si la valeur prise sinon définie naviguer dans le tableau booléen pour obtenir la valeur comme indiqué ci-dessous

package study;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/*
Created By Sachin  Rane on Jul 18, 2018
*/
public class UniqueRandomNumber {
    static Boolean[] boolArray;
    public static void main(String s[]){
        List<Integer> integers = new ArrayList<>();


        for (int i = 0; i < 10; i++) {
            integers.add(i);
        }


        //get unique random numbers
        boolArray = new Boolean[integers.size()+1];
        Arrays.fill(boolArray, false);
        for (int i = 0; i < 10; i++) {
            System.out.print(getUniqueRandomNumber(integers) + " ");

        }

    }

    private static int  getUniqueRandomNumber(List<Integer> integers) {
        int randNum =(int) (Math.random()*integers.size());
        if(boolArray[randNum]){
            while(boolArray[randNum]){
                randNum++;
                if(randNum>boolArray.length){
                    randNum=0;
                }
            }
            boolArray[randNum]=true;
            return randNum;
        }else {
            boolArray[randNum]=true;
            return randNum;
        }

    }

}

0

Choisissez n nombres aléatoires uniques de 0 à m-1.

int[] uniqueRand(int n, int m){
    Random rand = new Random();
    int[] r = new int[n];
    int[] result = new int[n];
    for(int i = 0; i < n; i++){
        r[i] = rand.nextInt(m-i);
        result[i] = r[i];
        for(int j = i-1; j >= 0; j--){
            if(result[i] >= r[j])
                result[i]++;
        }
    }
    return result;
}

Imaginez une liste contenant des nombres de 0 à m-1. Pour choisir le premier nombre, nous utilisons simplement rand.nextInt(m). Supprimez ensuite le numéro de la liste. Maintenant, il reste des numéros m-1, alors nous appelons rand.nextInt(m-1). Le nombre que nous obtenons représente la position dans la liste. S'il est inférieur au premier nombre, alors c'est le deuxième nombre, puisque la partie de la liste précédant le premier nombre n'a pas été modifiée par la suppression du premier nombre. Si la position est supérieure ou égale au premier nombre, le deuxième nombre est la position + 1. Faites une dérivation supplémentaire, vous pouvez obtenir cet algorithme.

Explication

Cet algorithme a une complexité O (n ^ 2). Il est donc utile de générer une petite quantité de nombres uniques à partir d'un grand ensemble. Alors que l'algorithme basé sur le shuffle a besoin d'au moins O (m) pour effectuer le shuffle.

L'algorithme basé sur le shuffle a également besoin de mémoire pour stocker tous les résultats possibles pour effectuer le shuffle, cet algorithme n'en a pas besoin.


0

Vous pouvez utiliser la classe Collections.

Une classe utilitaire appelée Collections propose différentes actions qui peuvent être effectuées sur une collection comme une ArrayList (par exemple, rechercher les éléments, trouver l'élément maximum ou minimum, inverser l'ordre des éléments, etc.). L'une des actions qu'il peut effectuer est de mélanger les éléments. La lecture aléatoire déplacera chaque élément de manière aléatoire vers une position différente dans la liste. Il le fait en utilisant un objet Random. Cela signifie que c'est un hasard déterministe, mais cela fonctionnera dans la plupart des situations.

Pour mélanger ArrayList, ajoutez l'importation de collections en haut du programme, puis utilisez la méthode statique Shuffle. Il faut que ArrayList soit mélangé en tant que paramètre:

import java.util.Collections;
import java.util.ArrayList;
public class Lottery {
public static void main(String[] args) {
//define ArrayList to hold Integer objects
ArrayList numbers = new ArrayList();
for(int i = 0; i < 100; i++)
{
numbers.add(i+1);
}
Collections.shuffle(numbers);
System.out.println(numbers);
}
}

0

Bien que ce soit un vieux fil, mais l'ajout d'une autre option pourrait ne pas nuire. (Les fonctions lambda du JDK 1.8 semblent faciliter les choses);

Le problème peut être décomposé selon les étapes suivantes;

  • Obtenez une valeur minimale pour la liste d'entiers fournie (pour laquelle générer des nombres aléatoires uniques)
  • Obtenir une valeur maximale pour la liste d'entiers fournie
  • Utilisez la classe ThreadLocalRandom (à partir de JDK 1.8) pour générer des valeurs entières aléatoires par rapport aux valeurs entières min et max précédemment trouvées, puis filtrez pour vous assurer que les valeurs sont bien contenues dans la liste fournie à l'origine. Enfin, appliquez distinct à l'intstream pour vous assurer que les numéros générés sont uniques.

Voici la fonction avec une description:

/**
 * Provided an unsequenced / sequenced list of integers, the function returns unique random IDs as defined by the parameter
 * @param numberToGenerate
 * @param idList
 * @return List of unique random integer values from the provided list
 */
private List<Integer> getUniqueRandomInts(List<Integer> idList, Integer numberToGenerate) {

    List<Integer> generatedUniqueIds = new ArrayList<>();

    Integer minId = idList.stream().mapToInt (v->v).min().orElseThrow(NoSuchElementException::new);
    Integer maxId = idList.stream().mapToInt (v->v).max().orElseThrow(NoSuchElementException::new);

            ThreadLocalRandom.current().ints(minId,maxId)
            .filter(e->idList.contains(e))
            .distinct()
            .limit(numberToGenerate)
            .forEach(generatedUniqueIds:: add);

    return generatedUniqueIds;

}

Ainsi, pour obtenir 11 nombres aléatoires uniques pour l'objet de liste 'allIntegers', nous appellerons la fonction comme;

    List<Integer> ids = getUniqueRandomInts(allIntegers,11);

La fonction déclare le nouveau arrayList 'generatedUniqueIds' et remplit chaque entier aléatoire unique jusqu'au nombre requis avant de retourner.

La classe PS ThreadLocalRandom évite la valeur de départ commune en cas de threads simultanés.


0

Il s'agit de la méthode la plus simple pour générer des valeurs aléatoires uniques dans une plage ou à partir d'un tableau .

Dans cet exemple, j'utiliserai un tableau prédéfini, mais vous pouvez également adapter cette méthode pour générer des nombres aléatoires. Tout d'abord, nous allons créer un exemple de tableau à partir duquel récupérer nos données.

  1. Générez un nombre aléatoire et ajoutez-le au nouveau tableau.
  2. Générez un autre nombre aléatoire et vérifiez s'il est déjà stocké dans le nouveau tableau.
  3. Sinon, ajoutez-le et continuez
  4. sinon réitérez l'étape.
ArrayList<Integer> sampleList = new ArrayList<>();
sampleList.add(1);
sampleList.add(2);
sampleList.add(3);
sampleList.add(4);
sampleList.add(5);
sampleList.add(6);
sampleList.add(7);
sampleList.add(8);

Maintenant, à partir du, sampleListnous allons produire cinq nombres aléatoires uniques.

int n;
randomList = new ArrayList<>();
for(int  i=0;i<5;i++){
    Random random = new Random();
    n=random.nextInt(8);     //Generate a random index between 0-7

    if(!randomList.contains(sampleList.get(n)))
    randomList.add(sampleList.get(n));
    else
        i--;    //reiterating the step
}
        

C'est conceptuellement très simple. Si la valeur aléatoire générée existe déjà, nous réitérerons l'étape. Cela continuera jusqu'à ce que toutes les valeurs générées soient uniques.

Si vous avez trouvé cette réponse utile, vous pouvez la voter car son concept est beaucoup plus simple que les autres réponses .


-1

Vous pouvez générer n nombre aléatoire unique entre 0 et n-1 en java

public static void RandomGenerate(int n)
{
     Set<Integer> st=new HashSet<Integer>();
     Random r=new Random();
     while(st.size()<n)
     {
        st.add(r.nextInt(n));
     }

}


-2

Vérifie ça

public class RandomNumbers {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int n = 5;
        int A[] = uniqueRandomArray(n);
        for(int i = 0; i<n; i++){
            System.out.println(A[i]);
        }
    }
    public static int[] uniqueRandomArray(int n){
        int [] A = new int[n];
        for(int i = 0; i< A.length; ){
            if(i == A.length){
                break;
            }
            int b = (int)(Math.random() *n) + 1;
            if(f(A,b) == false){
                A[i++] = b;
            } 
        }
        return A;
    }
    public static boolean f(int[] A, int n){
        for(int i=0; i<A.length; i++){
            if(A[i] == n){
                return true;
            }
        }
        return false;
    }
}

2
Jeter les normes Java, la lisibilité et la convivialité par la fenêtre, hein?
austin wernli

Le code n'est pas une réponse. Vous écrivez une réponse, puis vous ajoutez du code pour expliquer ce que vous vouliez.
Aditya

-2

Voici une façon dont j'avais l'habitude de générer toujours un numéro unique. La fonction aléatoire génère un nombre et le stocke dans un fichier texte, puis la prochaine fois qu'elle le vérifie dans le fichier, le compare et génère un nouveau numéro unique, de sorte qu'il y a toujours un nouveau numéro unique.

public int GenerateRandomNo()
{
    int _min = 0000;
    int _max = 9999;
    Random _rdm = new Random();
    return _rdm.Next(_min, _max);
}
public int rand_num()
{
    randnum = GenerateRandomNo();
    string createText = randnum.ToString() + Environment.NewLine;
    string file_path = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + @"\Invoices\numbers.txt";
    File.AppendAllText(file_path, createText);
    int number = File.ReadLines(file_path).Count(); //count number of lines in file
    System.IO.StreamReader file = new System.IO.StreamReader(file_path);
    do
    {
        randnum = GenerateRandomNo();
    }
    while ((file.ReadLine()) == randnum.ToString());
    file.Close();
    return randnum;

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.