Generación de números aleatorios únicos en Java


88

Estoy tratando de obtener números aleatorios entre 0 y 100. Pero quiero que sean únicos, no repetidos en una secuencia. Por ejemplo, si tengo 5 números, deberían ser 82,12,53,64,32 y no 82,12,53,12,32. Usé esto, pero genera los mismos números en una secuencia.

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

5
Puede crear una permutación aleatoria del rango 1..100(existen algoritmos famosos para eso), pero deténgase después de haber determinado los primeros nelementos.
Kerrek SB



Respuestas:


145
  • Agregue cada número en el rango secuencialmente en una estructura de lista .
  • Mezclarlo .
  • Toma la primera 'n'.

Aquí hay una implementación simple. Esto imprimirá 3 números aleatorios únicos del rango 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 primera parte de la solución con el enfoque original, como señaló Mark Byers en una respuesta ahora eliminada, es usar solo una Randominstancia.

Eso es lo que hace que los números sean idénticos. Una Randominstancia es sembrada por la hora actual en milisegundos. Para un valor semilla en particular , la instancia 'aleatoria' devolverá exactamente la misma secuencia de números pseudoaleatorios .

TENGA EN CUENTA que el public Integer​(int value)constructor es deprecateddesde Java 9.

El primer bucle for simplemente se puede cambiar a:

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

3
+1 por señalar una única instancia aleatoria y responder la pregunta. :)
Mark Byers

No tienes que mezclar todo el rango. Si desea n números únicos, entonces solo necesita mezclar la primera posición n usando una combinación de Fisher-Yates. Esto puede ayudar con una lista grande y una n pequeña.
rossum

59

Con Java 8+, puede usar el intsmétodo de Randompara obtener una cantidad IntStreamde valores aleatorios distincty limitreducir la secuencia a una cantidad de valores aleatorios únicos.

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

Randomtambién tiene métodos que crean LongStreamsy DoubleStreams si los necesita en su lugar.

Si desea todos (o una gran cantidad) de los números en un rango en un orden aleatorio, podría ser más eficiente agregar todos los números a una lista, mezclarla y tomar la primera n porque el ejemplo anterior está implementado actualmente generando números aleatorios en el rango solicitado y pasándolos a través de un conjunto (de manera similar a la respuesta de Rob Kielty ), lo que puede requerir generar muchos más que la cantidad pasada al límite porque la probabilidad de generar un nuevo número único disminuye con cada uno encontrado. Aquí hay un ejemplo de la otra forma:

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

Necesitaba esto para un código que estoy evaluando y Arrays#setAll()es un poco más rápido que una transmisión. Entonces: `Integer [] índices = new Integer [n]; Arrays.setAll (índices, i -> i); Colecciones.shuffle (Arrays.asList (índices)); return Arrays.stream (índices) .mapToInt (Integer :: intValue) .toArray (); `
AbuNassar

18
  1. Cree una matriz de 100 números y luego aleatorice su orden.
  2. Diseñe un generador de números pseudoaleatorios que tenga un rango de 100.
  3. Cree una matriz booleana de 100 elementos, luego establezca un elemento como verdadero cuando elija ese número. Cuando elija el siguiente número, verifique con la matriz y vuelva a intentarlo si el elemento de la matriz está configurado. (Puede crear una matriz booleana fácil de borrar con una matriz de longdónde cambia y enmascara para acceder a bits individuales).

2
+1 para la aproximación alternativa; pick()es un ejemplo.
trashgod

1
En lugar de usar una matriz booleana, podría usar a HashSet, donde almacena los números que ya ha generado y usa containspara probar si ya ha generado ese número. El HashSetprobablemente será ligeramente más lento que una matriz booleana, pero ocupan menos memoria.
Rory O'Kane

1
@ RoryO'Kane - Estoy bastante seguro de que la matriz booleana ocuparía menos espacio, si se implementa como una matriz de long [2]. De ninguna manera podrías hacer un HashSet tan pequeño.
Hot Licks

El último enfoque es un poco desagradable ya que no tendría un número bien definido de pasos para generar la secuencia completa. Además, no necesita reinventar la rueda: BitSet .
Pavel Horal

16

Utilice Collections.shuffle()en los 100 números y seleccione los primeros cinco, como se muestra aquí .


13

Siento que vale la pena mencionar este método.

   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

Refactoricé la respuesta de Anand para hacer uso no solo de las propiedades únicas de un conjunto, sino que también utilicé el booleano falso devuelto por set.add()cuando falla una adición al conjunto.

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
Buena idea. Sin embargo, una marca importante, si SET_SIZE_REQUIREDes lo suficientemente grande (digamos, más que NUMBER_RANGE / 2entonces, tiene un tiempo de ejecución esperado mucho mayor.
Noamgot

5

He hecho esto así.

    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

Esto funcionará para generar números aleatorios únicos ................

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

Una forma inteligente de hacer esto es usar exponentes de un elemento primitivo en módulo.

Por ejemplo, 2 es un mod de raíz primitivo 101, lo que significa que los poderes de 2 mod 101 te dan una secuencia no repetitiva que ve todos los números del 1 al 100 inclusive:

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

En código Java, escribirías:

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

Encontrar una raíz primitiva para un módulo específico puede ser complicado, pero la función "primroot" de Maple lo hará por usted.


Eso es interesante, pero ¿cómo nos aseguraríamos de que la secuencia generada sea aleatoria? No parece serlo. Parece muy determinista tener 1, 2, 4, 8, 16, ... al comienzo de una secuencia.
h4nek

No es aleatorio ... es pseudoaleatorio. Nadie sabe cómo generar números verdaderamente aleatorios. Si no le gusta el patrón inicial, puede usar una base más grande como raíz primitiva.
AT - estudiante

Pseudoaleatorio estaría bien. Pero aquí, para un "rango" dado, la cantidad de raíces primitivas y por lo tanto secuencias únicas es limitada, especialmente para rangos más pequeños. Así que parece haber un problema con el patrón, por ejemplo, tener siempre una subsecuencia de potencias de la raíz. Y no obtener una secuencia (probablemente) muy diferente en múltiples ejecuciones, a menos que apliquemos algunas travesuras más. Supongo que depende del caso de uso. Cambiar la base es una buena mejora de todos modos, aunque solo "cambia" el patrón.
h4nek

2

Vine aquí de otra pregunta, que ha sido duplicada de esta pregunta ( Generando un número aleatorio único en Java )

  1. Almacene de 1 a 100 números en una matriz.

  2. Genere un número aleatorio entre 1 y 100 como posición y devuelva la matriz [posición-1] para obtener el valor

  3. Una vez que use un número en la matriz, marque el valor como -1 (No es necesario mantener otra matriz para verificar si este número ya se usa)

  4. Si el valor en la matriz es -1, obtenga el número aleatorio nuevamente para buscar una nueva ubicación en la matriz.


2

Tengo una solución fácil para este problema. Con esto podemos generar fácilmente n números aleatorios únicos. Es lógico que cualquiera pueda usarlo en cualquier idioma.

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

puede optimizar haciendo un break;after thei—;
Jan

0

probar esto

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

Esto no es significativamente diferente de otras respuestas, pero quería la matriz de enteros al final:

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

0

puede usar una matriz booleana para completar el valor verdadero si se toma el valor; de lo contrario, configure navegue a través de la matriz booleana para obtener el valor como se indica a continuación

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

Elija n números aleatorios únicos de 0 a 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;
}

Imagine una lista que contiene números del 0 al m-1. Para elegir el primer número, simplemente usamosrand.nextInt(m) . Luego elimine el número de la lista. Ahora quedan números m-1, así que llamamosrand.nextInt(m-1) . El número que obtenemos representa la posición en la lista. Si es menor que el primer número, entonces es el segundo número, ya que la parte de la lista anterior al primer número no se modificó al eliminar el primer número. Si la posición es mayor o igual que el primer número, el segundo número es la posición + 1. Haga una derivación adicional, puede obtener este algoritmo.

Explicación

Este algoritmo tiene complejidad O (n ^ 2). Por lo tanto, es bueno para generar una pequeña cantidad de números únicos a partir de un conjunto grande. Mientras que el algoritmo basado en la reproducción aleatoria necesita al menos O (m) para realizar la reproducción aleatoria.

Además, el algoritmo basado en la reproducción aleatoria necesita memoria para almacenar todos los resultados posibles para realizar la reproducción aleatoria, este algoritmo no es necesario.


0

Puede utilizar la clase Colecciones.

Una clase de utilidad llamada Colecciones ofrece diferentes acciones que se pueden realizar en una colección como ArrayList (por ejemplo, buscar los elementos, encontrar el elemento máximo o mínimo, invertir el orden de los elementos, etc.). Una de las acciones que puede realizar es mezclar los elementos. La reproducción aleatoria moverá aleatoriamente cada elemento a una posición diferente en la lista. Lo hace utilizando un objeto aleatorio. Esto significa que es una aleatoriedad determinista, pero funcionará en la mayoría de las situaciones.

Para mezclar ArrayList, agregue la importación de Colecciones en la parte superior del programa y luego use el método estático Shuffle. Se necesita ArrayList para barajar como parámetro:

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

Aunque es un hilo antiguo, es posible que agregar otra opción no sea perjudicial. (Las funciones lambda de JDK 1.8 parecen facilitar las cosas);

El problema se puede dividir en los siguientes pasos;

  • Obtenga un valor mínimo para la lista de números enteros proporcionada (para la cual generar números aleatorios únicos)
  • Obtenga un valor máximo para la lista de enteros proporcionada
  • Use la clase ThreadLocalRandom (de JDK 1.8) para generar valores enteros aleatorios contra los valores enteros mínimo y máximo encontrados anteriormente y luego filtre para asegurarse de que los valores estén contenidos en la lista proporcionada originalmente. Finalmente, aplique distinto al intstream para garantizar que los números generados sean únicos.

Aquí está la función con una descripción:

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

}

Entonces, para obtener 11 números aleatorios únicos para el objeto de lista 'allIntegers', llamaremos a la función como;

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

La función declara un nuevo arrayList 'generateUniqueIds' y se llena con cada entero aleatorio único hasta el número requerido antes de regresar.

La clase PS ThreadLocalRandom evita el valor de inicialización común en el caso de subprocesos simultáneos.


0

Este es el método más simple para generar valores aleatorios únicos en un rango o de una matriz .

En este ejemplo, usaré una matriz predefinida, pero también puede adaptar este método para generar números aleatorios. Primero, crearemos una matriz de muestra para recuperar nuestros datos.

  1. Genere un número aleatorio y agréguelo a la nueva matriz.
  2. Genere otro número aleatorio y verifique si ya está almacenado en la nueva matriz.
  3. Si no, agréguelo y continúe
  4. más reiterar el paso.
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);

A partir de ahora sampleList, produciremos cinco números aleatorios que son únicos.

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
}
        

Esto es conceptualmente muy simple. Si el valor aleatorio generado ya existe, reiteraremos el paso. Esto continuará hasta que todos los valores generados sean únicos.

Si encuentra útil esta respuesta, puede votarla, ya que es mucho más simple en concepto en comparación con las otras respuestas .


-1

Puede generar n números aleatorios únicos entre 0 y 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

Mira esto

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
Tirando por la ventana los estándares, la legibilidad y la usabilidad de Java, ¿eh?
Austin Wernli

El código no es una respuesta. Escribe una respuesta y luego agrega código para explicar lo que querías.
Aditya

-2

A continuación se muestra una forma que solía generar siempre un número único. La función aleatoria genera un número y lo almacena en un archivo de texto, luego la próxima vez que lo verifica en el archivo lo compara y genera un nuevo número único, por lo que de esta manera siempre hay un nuevo número único.

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;

}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.