Creando números aleatorios sin duplicados

88

En este caso, el MAX es solo 5, por lo que podría verificar los duplicados uno por uno, pero ¿cómo podría hacer esto de una manera más simple? Por ejemplo, ¿qué pasa si el MAX tiene un valor de 20? Gracias.

int MAX = 5;

for (i = 1 , i <= MAX; i++)
{
        drawNum[1] = (int)(Math.random()*MAX)+1;

        while (drawNum[2] == drawNum[1])
        {
             drawNum[2] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[3] == drawNum[1]) || (drawNum[3] == drawNum[2]) )
        {
             drawNum[3] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[4] == drawNum[1]) || (drawNum[4] == drawNum[2]) || (drawNum[4] == drawNum[3]) )
        {
             drawNum[4] = (int)(Math.random()*MAX)+1;
        }
        while ((drawNum[5] == drawNum[1]) ||
               (drawNum[5] == drawNum[2]) ||
               (drawNum[5] == drawNum[3]) ||
               (drawNum[5] == drawNum[4]) )
        {
             drawNum[5] = (int)(Math.random()*MAX)+1;
        }

}
Woong-Sup Jung
fuente
3
Muchos (pseudo) generadores de números aleatorios no se repiten durante su "ciclo" completo. El problema es, por supuesto, que su "ciclo" completo es de miles de millones o billones de valores, y los valores que producen pueden ser cualquiera de esos miles de millones o billones de valores. En teoría, podría producir un generador de números aleatorios que tuviera un "ciclo" de 5 o 10 o lo que sea, pero probablemente sea más problemático de lo que vale.
Hot Licks
1
Además, un generador de números aleatorios que no se repite es incluso "menos" aleatorio: si MAX = 5 y lee 3 números, puede adivinar el siguiente con un 50% de probabilidad, si lee 4 números, sabe el siguiente para el 100% ¡Por supuesto!
icza
Respondido en una pregunta duplicada aquí
Alex - GlassEditor.com

Respuestas:

149

La forma más sencilla sería crear una lista de los números posibles (1..20 o lo que sea) y luego mezclarlos con Collections.shuffle. Luego, simplemente tome tantos elementos como desee. Esto es genial si tu rango es igual al número de elementos que necesitas al final (por ejemplo, para barajar una baraja de cartas).

Eso no funciona tan bien si quieres (digamos) 10 elementos aleatorios en el rango 1..10,000 - terminarías haciendo mucho trabajo innecesariamente. En ese punto, probablemente sea mejor mantener un conjunto de valores que ha generado hasta ahora y seguir generando números en un ciclo hasta que el siguiente no esté ya presente:

if (max < numbersNeeded)
{
    throw new IllegalArgumentException("Can't ask for more numbers than are available");
}
Random rng = new Random(); // Ideally just create one instance globally
// Note: use LinkedHashSet to maintain insertion order
Set<Integer> generated = new LinkedHashSet<Integer>();
while (generated.size() < numbersNeeded)
{
    Integer next = rng.nextInt(max) + 1;
    // As we're adding to a set, this will automatically do a containment check
    generated.add(next);
}

Sin embargo, tenga cuidado con la elección del conjunto: lo he usado deliberadamente, LinkedHashSetya que mantiene el orden de inserción, lo que nos importa aquí.

Otra opción más es progresar siempre , reduciendo el rango cada vez y compensando los valores existentes. Entonces, por ejemplo, suponga que desea 3 valores en el rango 0..9. En la primera iteración, generaría cualquier número en el rango 0..9; digamos que genera un 4.

En la segunda iteración, generaría un número en el rango 0..8. Si el número generado es menor que 4, lo mantendrá como está ... de lo contrario, le agregará uno. Eso le da un rango de resultado de 0..9 sin 4. Suponga que obtenemos 7 de esa manera.

En la tercera iteración, generaría un número en el rango 0..7. Si el número generado es menor que 4, lo mantendrá como está. Si es 4 o 5, agregaría uno. Si es 6 o 7, agregaría dos. De esa manera, el rango de resultados es 0..9 sin 4 o 6.

Jon Skeet
fuente
Genere una matriz de los valores posibles, seleccione aleatoriamente uno (tamaño de matriz de modulación de número aleatorio), elimine (y guarde) el número seleccionado, luego repita.
Hot Licks
O use un generador aleatorio con un ciclo completo (los basados ​​en números primos pueden usar números primos pequeños, con los ciclos pequeños correspondientes) y eliminar valores fuera de rango.
Paul de Vrieze
La "Otra opción más es progresar siempre" es WAAAAY la mejor solución. Edite para reflejar. Y gracias por esta increíble respuesta.
user123321
1
@musselwhizzle: Intentaré encontrar tiempo pronto. Sin embargo, no estoy seguro de "WAAAY mejor"; será significativamente menos "obviamente correcto", aunque será más eficiente. Muy a menudo me complace sacrificar el rendimiento en aras de la legibilidad.
Jon Skeet
@Deepthi: el máximo que desee el OP, según la pregunta.
Jon Skeet
19

Así es como lo haría

import java.util.ArrayList;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        int size = 20;

        ArrayList<Integer> list = new ArrayList<Integer>(size);
        for(int i = 1; i <= size; i++) {
            list.add(i);
        }

        Random rand = new Random();
        while(list.size() > 0) {
            int index = rand.nextInt(list.size());
            System.out.println("Selected: "+list.remove(index));
        }
    }
}

Como ha señalado el estimado Sr. Skeet:
Si n es el número de números seleccionados al azar que desea elegir y N es el espacio muestral total de números disponibles para la selección:

  1. Si n << N , simplemente debe almacenar los números que ha elegido y verificar una lista para ver si el número seleccionado está en ella.
  2. Si n ~ = N , probablemente debería usar mi método, rellenando una lista que contenga todo el espacio muestral y luego eliminando números a medida que los selecciona.
Catchwa
fuente
La lista debe ser una LinkedList, eliminar índices aleatorios de la lista de matrices es muy ineficiente
Riccardo Casatta
@RiccardoCasatta ¿tiene una fuente para su afirmación? Tampoco puedo imaginar que recorrer una lista enlazada sea muy eficaz. Ver también: stackoverflow.com/a/6103075/79450
Catchwa
Lo probé y tienes razón, ¿debo borrar mi comentario?
Riccardo Casatta
@RiccardoCasatta Otros pueden encontrar útil nuestra ida y vuelta
Catchwa
13
//random numbers are 0,1,2,3 
ArrayList<Integer> numbers = new ArrayList<Integer>();   
Random randomGenerator = new Random();
while (numbers.size() < 4) {

    int random = randomGenerator .nextInt(4);
    if (!numbers.contains(random)) {
        numbers.add(random);
    }
}
Satheesh Guduri
fuente
Esto tendría un rendimiento horrible para grandes números. ArrayList.contains está iterando a través de la lista. Mucho más limpio sería tener un conjunto en su lugar: no es necesario verificar si contiene, solo agregue y el rendimiento será mejor.
kfox
5

Hay otra forma de hacer números ordenados "aleatorios" con LFSR, eche un vistazo a:

http://en.wikipedia.org/wiki/Linear_feedback_shift_register

con esta técnica puedes conseguir el número aleatorio ordenado por índice y asegurándote de que los valores no estén duplicados.

Pero estos no son números aleatorios VERDADEROS porque la generación aleatoria es determinista.

Pero dependiendo de su caso , puede usar esta técnica para reducir la cantidad de procesamiento en la generación de números aleatorios cuando usa la mezcla.

Aquí un algoritmo LFSR en Java, (lo llevé a algún lugar que no recuerdo):

public final class LFSR {
    private static final int M = 15;

    // hard-coded for 15-bits
    private static final int[] TAPS = {14, 15};

    private final boolean[] bits = new boolean[M + 1];

    public LFSR() {
        this((int)System.currentTimeMillis());
    }

    public LFSR(int seed) {
        for(int i = 0; i < M; i++) {
            bits[i] = (((1 << i) & seed) >>> i) == 1;
        }
    }

    /* generate a random int uniformly on the interval [-2^31 + 1, 2^31 - 1] */
    public short nextShort() {
        //printBits();

        // calculate the integer value from the registers
        short next = 0;
        for(int i = 0; i < M; i++) {
            next |= (bits[i] ? 1 : 0) << i;
        }

        // allow for zero without allowing for -2^31
        if (next < 0) next++;

        // calculate the last register from all the preceding
        bits[M] = false;
        for(int i = 0; i < TAPS.length; i++) {
            bits[M] ^= bits[M - TAPS[i]];
        }

        // shift all the registers
        for(int i = 0; i < M; i++) {
            bits[i] = bits[i + 1];
        }

        return next;
    }

    /** returns random double uniformly over [0, 1) */
    public double nextDouble() {
        return ((nextShort() / (Integer.MAX_VALUE + 1.0)) + 1.0) / 2.0;
    }

    /** returns random boolean */
    public boolean nextBoolean() {
        return nextShort() >= 0;
    }

    public void printBits() {
        System.out.print(bits[M] ? 1 : 0);
        System.out.print(" -> ");
        for(int i = M - 1; i >= 0; i--) {
            System.out.print(bits[i] ? 1 : 0);
        }
        System.out.println();
    }


    public static void main(String[] args) {
        LFSR rng = new LFSR();
        Vector<Short> vec = new Vector<Short>();
        for(int i = 0; i <= 32766; i++) {
            short next = rng.nextShort();
            // just testing/asserting to make 
            // sure the number doesn't repeat on a given list
            if (vec.contains(next))
                throw new RuntimeException("Index repeat: " + i);
            vec.add(next);
            System.out.println(next);
        }
    }
}
felipe
fuente
4

Otro enfoque que le permite especificar con cuántos números desea sizey los valores miny maxde los números devueltos

public static int getRandomInt(int min, int max) {
    Random random = new Random();

    return random.nextInt((max - min) + 1) + min;
}

public static ArrayList<Integer> getRandomNonRepeatingIntegers(int size, int min,
        int max) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();

    while (numbers.size() < size) {
        int random = getRandomInt(min, max);

        if (!numbers.contains(random)) {
            numbers.add(random);
        }
    }

    return numbers;
}

Para usarlo devuelve 7 números entre 0 y 25.

    ArrayList<Integer> list = getRandomNonRepeatingIntegers(7, 0, 25);
    for (int i = 0; i < list.size(); i++) {
        System.out.println("" + list.get(i));
    }
Carlo Rodríguez
fuente
4

Esto sería mucho más sencillo en java-8:

Stream.generate(new Random()::ints)
            .distinct()
            .limit(16) // whatever limit you might need
            .toArray(Integer[]::new);
Eugenio
fuente
3

La forma más eficiente y básica de tener números aleatorios no repetidos se explica mediante este pseudocódigo. No es necesario tener bucles anidados o búsquedas hash:

// get 5 unique random numbers, possible values 0 - 19
// (assume desired number of selections < number of choices)

const int POOL_SIZE = 20;
const int VAL_COUNT = 5;

declare Array mapping[POOL_SIZE];
declare Array results[VAL_COUNT];

declare i int;
declare r int;
declare max_rand int;

// create mapping array
for (i=0; i<POOL_SIZE; i++) {
   mapping[i] = i;
}

max_rand = POOL_SIZE-1;  // start loop searching for maximum value (19)

for (i=0; i<VAL_COUNT; i++) {
    r = Random(0, max_rand); // get random number
    results[i] = mapping[r]; // grab number from map array
    mapping[r] = max_rand;  // place item past range at selected location

    max_rand = max_rand - 1;  // reduce random scope by 1
}

Suponga que la primera iteración generó el número aleatorio 3 para comenzar (de 0 a 19). Esto haría que los resultados [0] = mapeo [3], es decir, el valor 3. Luego asignaríamos el mapeo [3] a 19.

En la siguiente iteración, el número aleatorio fue 5 (de 0 a 18). Esto haría que los resultados [1] = mapeo [5], es decir, el valor 5. Luego asignaríamos el mapeo [5] a 18.

Ahora suponga que la siguiente iteración elige 3 nuevamente (de 0 a 17). A los resultados [2] se les asignaría el valor de mapeo [3], pero ahora, este valor no es 3, sino 19.

Esta misma protección persiste para todos los números, incluso si obtuvo el mismo número 5 veces seguidas. Por ejemplo, si el generador de números aleatorios le dio 0 cinco veces seguidas, los resultados serían: [0, 19, 18, 17, 16].

Nunca obtendrías el mismo número dos veces.

blackcatweb
fuente
Dudo que esto sea tan aleatorio como parece. ¿Pasa las pruebas estándar de aleatoriedad ?; parecería concentrar números cerca del final del espectro.
Tucuxi
Este es un caso base. El grupo es {a, b, c}. Necesitamos 2 elementos no repetidos. Siguiendo el algoritmo, aquí hay combinaciones que podríamos dibujar y sus resultados: 0,0: a, c 0,1: a, b 1,0: b, a 1,1: b, c 2,0: c, a 2, 1: c, b Puntuación: a-4, b-4, c-4
blackcatweb
3

Generar todos los índices de una secuencia es generalmente una mala idea, ya que puede llevar mucho tiempo, especialmente si la proporción de los números a elegir MAXes baja (la complejidad se vuelve dominada por O(MAX)). Esto empeora si la relación de los números a elegir se MAXacerca a uno, ya que entonces eliminar los índices elegidos de la secuencia de todos también se vuelve costoso (nos acercamosO(MAX^2/2) ). Pero para números pequeños, esto generalmente funciona bien y no es particularmente propenso a errores.

Filtrar los índices generados mediante el uso de una colección también es una mala idea, ya que se dedica algo de tiempo a insertar los índices en la secuencia, y el progreso no está garantizado ya que se puede extraer el mismo número aleatorio varias veces (pero para lo suficientemente grande MAXes poco probable ). Esto podría estar cerca de la complejidad
O(k n log^2(n)/2), ignorando los duplicados y asumiendo que la colección usa un árbol para una búsqueda eficiente (pero con un costo constante significativo kde asignar los nodos del árbol y posiblemente tener que reequilibrar ).

Otra opción es generar los valores aleatorios de forma única desde el principio, garantizando que se avanza. Eso significa que en la primera ronda, [0, MAX]se genera un índice aleatorio en :

items i0 i1 i2 i3 i4 i5 i6 (total 7 items)
idx 0       ^^             (index 2)

En la segunda ronda, solo [0, MAX - 1]se genera (ya que un elemento ya estaba seleccionado):

items i0 i1    i3 i4 i5 i6 (total 6 items)
idx 1          ^^          (index 2 out of these 6, but 3 out of the original 7)

Luego, los valores de los índices deben ajustarse: si el segundo índice cae en la segunda mitad de la secuencia (después del primer índice), debe incrementarse para tener en cuenta la brecha. Podemos implementar esto como un bucle, lo que nos permite seleccionar un número arbitrario de elementos únicos.

Para secuencias cortas, este es un O(n^2/2)algoritmo bastante rápido :

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3187.000 msec (the fastest)
    // b2: 3734.000 msec
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        size_t n_where = i;
        for(size_t j = 0; j < i; ++ j) {
            if(n + j < rand_num[j]) {
                n_where = j;
                break;
            }
        }
        // see where it should be inserted

        rand_num.insert(rand_num.begin() + n_where, 1, n + n_where);
        // insert it in the list, maintain a sorted sequence
    }
    // tier 1 - use comparison with offset instead of increment
}

¿Dónde n_select_numestá tu 5 y n_number_numes tu MAX. Los n_Rand(x)rendimientos enteros aleatorios en [0, x](ambos inclusive). Esto se puede hacer un poco más rápido si se seleccionan muchos elementos (por ejemplo, no 5 sino 500) mediante la búsqueda binaria para encontrar el punto de inserción. Para hacer eso, debemos asegurarnos de cumplir con los requisitos.

Haremos una búsqueda binaria con la comparación n + j < rand_num[j]que es igual a
n < rand_num[j] - j. Necesitamos mostrar que rand_num[j] - jsigue siendo una secuencia ordenada para una secuencia ordenada rand_num[j]. Afortunadamente, esto se muestra fácilmente, ya que la distancia más baja entre dos elementos del original rand_numes uno (los números generados son únicos, por lo que siempre hay una diferencia de al menos 1). Al mismo tiempo, si restamos los índices jde todos los elementos
rand_num[j], las diferencias en el índice son exactamente 1. Entonces, en el "peor" caso, obtenemos una secuencia constante, pero nunca decreciente. Por lo tanto, se puede utilizar la búsqueda binaria, dando como resultado el O(n log(n))algoritmo:

struct TNeedle { // in the comparison operator we need to make clear which argument is the needle and which is already in the list; we do that using the type system.
    int n;

    TNeedle(int _n)
        :n(_n)
    {}
};

class CCompareWithOffset { // custom comparison "n < rand_num[j] - j"
protected:
    std::vector<int>::iterator m_p_begin_it;

public:
    CCompareWithOffset(std::vector<int>::iterator p_begin_it)
        :m_p_begin_it(p_begin_it)
    {}

    bool operator ()(const int &r_value, TNeedle n) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return r_value < n.n + n_index; // or r_value - n_index < n.n
    }

    bool operator ()(TNeedle n, const int &r_value) const
    {
        size_t n_index = &r_value - &*m_p_begin_it;
        // calculate index in the array

        return n.n + n_index < r_value; // or n.n < r_value - n_index
    }
};

Y finalmente:

void RandomUniqueSequence(std::vector<int> &rand_num,
    const size_t n_select_num, const size_t n_item_num)
{
    assert(n_select_num <= n_item_num);

    rand_num.clear(); // !!

    // b1: 3578.000 msec
    // b2: 1703.000 msec (the fastest)
    for(size_t i = 0; i < n_select_num; ++ i) {
        int n = n_Rand(n_item_num - i - 1);
        // get a random number

        std::vector<int>::iterator p_where_it = std::upper_bound(rand_num.begin(), rand_num.end(),
            TNeedle(n), CCompareWithOffset(rand_num.begin()));
        // see where it should be inserted

        rand_num.insert(p_where_it, 1, n + p_where_it - rand_num.begin());
        // insert it in the list, maintain a sorted sequence
    }
    // tier 4 - use binary search
}

He probado esto en tres puntos de referencia. Primero, se eligieron 3 números de 7 elementos, y se acumuló un histograma de los elementos elegidos en más de 10,000 ejecuciones:

4265 4229 4351 4267 4267 4364 4257

Esto muestra que cada uno de los 7 elementos se eligió aproximadamente el mismo número de veces, y no existe un sesgo aparente causado por el algoritmo. También se verificó la exactitud de todas las secuencias (unicidad de los contenidos).

El segundo punto de referencia consistió en elegir 7 números de 5000 elementos. El tiempo de varias versiones del algoritmo se acumuló en 10,000,000 ejecuciones. Los resultados se indican en los comentarios del código como b1. La versión simple del algoritmo es un poco más rápida.

El tercer punto de referencia consistió en elegir 700 números de 5000 elementos. El tiempo de varias versiones del algoritmo se acumuló nuevamente, esta vez más de 10,000 ejecuciones. Los resultados se indican en los comentarios del código como b2. La versión de búsqueda binaria del algoritmo es ahora más de dos veces más rápida que la simple.

El segundo método comienza a ser más rápido para elegir más de cca 75 elementos en mi máquina (tenga en cuenta que la complejidad de cualquiera de los algoritmos no depende de la cantidad de elementos MAX).

Vale la pena mencionar que los algoritmos anteriores generan los números aleatorios en orden ascendente. Pero sería sencillo agregar otra matriz en la que se guardarían los números en el orden en que se generaron y devolverla en su lugar (con un costo adicional insignificante O(n)). No es necesario mezclar la salida: sería mucho más lento.

Tenga en cuenta que las fuentes están en C ++, no tengo Java en mi máquina, pero el concepto debería ser claro.

EDITAR :

Para divertirme, también he implementado el enfoque que genera una lista con todos los índices
0 .. MAX, los elige al azar y los elimina de la lista para garantizar la singularidad. Como elegí bastante alto MAX(5000), el rendimiento es catastrófico:

// b1: 519515.000 msec
// b2: 20312.000 msec
std::vector<int> all_numbers(n_item_num);
std::iota(all_numbers.begin(), all_numbers.end(), 0);
// generate all the numbers

for(size_t i = 0; i < n_number_num; ++ i) {
    assert(all_numbers.size() == n_item_num - i);
    int n = n_Rand(n_item_num - i - 1);
    // get a random number

    rand_num.push_back(all_numbers[n]); // put it in the output list
    all_numbers.erase(all_numbers.begin() + n); // erase it from the input
}
// generate random numbers

También implementé el enfoque con una set(una colección de C ++), que en realidad ocupa el segundo lugar en el punto de referencia b2, siendo solo un 50% más lento que el enfoque con la búsqueda binaria. Eso es comprensible, ya que setutiliza un árbol binario, donde el costo de inserción es similar al de la búsqueda binaria. La única diferencia es la posibilidad de obtener elementos duplicados, lo que ralentiza el progreso.

// b1: 20250.000 msec
// b2: 2296.000 msec
std::set<int> numbers;
while(numbers.size() < n_number_num)
    numbers.insert(n_Rand(n_item_num - 1)); // might have duplicates here
// generate unique random numbers

rand_num.resize(numbers.size());
std::copy(numbers.begin(), numbers.end(), rand_num.begin());
// copy the numbers from a set to a vector

El código fuente completo está aquí .

los cerdos
fuente
2

Puede usar una de las clases que implementan la interfaz Set ( API ), y luego, cada número que genere, use Set.add () para insertarlo.

Si el valor devuelto es falso, sabrá que el número ya se generó antes.

SSTwinrova
fuente
2

En lugar de hacer todo esto, cree un LinkedHashSetobjeto y números aleatorios por Math.random()función ... si ocurre alguna entrada duplicada, el LinkedHashSetobjeto no agregará ese número a su Lista ... Ya que en esta Clase de Colección no se permiten valores duplicados ... al final, obtienes una lista de números aleatorios que no tienen valores duplicados ....: D

abdeali chandan
fuente
2

Su problema parece reducirse a elegir k elementos al azar de una colección de n elementos. La respuesta de Collections.shuffle es, por lo tanto, correcta, pero como se señaló, ineficiente: es O (n).

Wikipedia: Fisher-Yates shuffle tiene una versión O (k) cuando la matriz ya existe. En su caso, no hay una matriz de elementos y crear la matriz de elementos podría ser muy costoso, digamos si el máximo fuera 10000000 en lugar de 20.

El algoritmo de reproducción aleatoria implica inicializar una matriz de tamaño n donde cada elemento es igual a su índice, seleccionando k números aleatorios de cada número en un rango con el máximo uno menos que el rango anterior, luego intercambiando elementos hacia el final de la matriz.

Puede hacer la misma operación en tiempo O (k) con un mapa de hash, aunque admito que es un poco molesto. Tenga en cuenta que esto solo vale la pena si k es mucho menor que n. (es decir, k ~ lg (n) más o menos), de lo contrario, debería utilizar la función aleatoria directamente.

Utilizará su mapa de hash como una representación eficiente de la matriz de respaldo en el algoritmo de reproducción aleatoria. Cualquier elemento de la matriz que sea igual a su índice no necesita aparecer en el mapa. Esto le permite representar una matriz de tamaño n en tiempo constante, no hay tiempo para inicializarla.

  1. Elija k números aleatorios: el primero está en el rango de 0 a n-1, el segundo de 0 a n-2, el tercero de 0 a n-3 y así sucesivamente, a través de nk.

  2. Trate sus números aleatorios como un conjunto de intercambios. El primer índice aleatorio cambia a la posición final. El segundo índice aleatorio cambia a la penúltima posición. Sin embargo, en lugar de trabajar contra una matriz de respaldo, trabaje contra su mapa de hash. Su mapa de hash almacenará todos los elementos que estén fuera de posición.

int getValue(i) { if (map.contains(i)) return map[i]; return i; } void setValue(i, val) { if (i == val) map.remove(i); else map[i] = val; } int[] chooseK(int n, int k) { for (int i = 0; i < k; i++) { int randomIndex = nextRandom(0, n - i); //(n - i is exclusive) int desiredIndex = n-i-1; int valAtRandom = getValue(randomIndex); int valAtDesired = getValue(desiredIndex); setValue(desiredIndex, valAtRandom); setValue(randomIndex, valAtDesired); } int[] output = new int[k]; for (int i = 0; i < k; i++) { output[i] = (getValue(n-i-1)); } return output; }

dhakim
fuente
creating the array of elements could be very expensive- ¿Por qué debería ser más caro crear una matriz que barajar? Creo que no hay absolutamente ninguna razón para el pesimismo en este punto :-)
Wolf
1

El siguiente código crea una secuencia de números aleatorios entre [1, m] que no se generó antes.

public class NewClass {

    public List<Integer> keys = new ArrayList<Integer>();

    public int rand(int m) {
        int n = (int) (Math.random() * m + 1);
        if (!keys.contains(n)) {
            keys.add(n);
            return n;
        } else {
            return rand(m);
        }
    }

    public static void main(String[] args) {
        int m = 4;
        NewClass ne = new NewClass();
        for (int i = 0; i < 4; i++) {
            System.out.println(ne.rand(m));
        }
        System.out.println("list: " + ne.keys);
    }
}
ParisaN
fuente
0

Hay un algoritmo de lote de tarjetas: usted crea una matriz ordenada de números (el "lote de tarjetas") y en cada iteración selecciona un número en una posición aleatoria (eliminando el número seleccionado del "lote de tarjetas", por supuesto).

Lavir el Whiolet
fuente
0

Esta es una solución eficiente para la creación rápida de una matriz aleatoria. Después de la aleatorización, simplemente puede elegir el n-ésimo elemento ede la matriz, incrementar ny regresar e. Esta solución tiene O (1) para obtener un número aleatorio y O (n) para la inicialización, pero como compensación requiere una buena cantidad de memoria si n se vuelve lo suficientemente grande.

Martin Thurau
fuente
0

Existe una solución más eficiente y menos engorrosa para números enteros que Collections.shuffle.

El problema es el mismo que elegir sucesivamente elementos de solo los elementos no seleccionados de un conjunto y ponerlos en orden en otro lugar. Esto es exactamente como repartir cartas al azar o sacar boletos de rifa ganadores de un sombrero o papelera.

Este algoritmo funciona para cargar cualquier arreglo y lograr un orden aleatorio al final de la carga. También funciona para agregar a una colección List (o cualquier otra colección indexada) y lograr una secuencia aleatoria en la colección al final de las adiciones.

Se puede hacer con una sola matriz, creada una vez, o con una colección ordenada numéricamente, como una Lista, en su lugar. Para una matriz, el tamaño de la matriz inicial debe ser el tamaño exacto para contener todos los valores previstos. Si no sabe cuántos valores pueden ocurrir de antemano, también funcionará el uso de una colección ordenada numéricamente, como ArrayList o List, donde el tamaño no es inmutable. Funcionará universalmente para una matriz de cualquier tamaño hasta Integer.MAX_VALUE, que es un poco más de 2,000,000,000. Los objetos de lista tendrán los mismos límites de índice. Su máquina puede quedarse sin memoria antes de que llegue a una matriz de ese tamaño. Puede ser más eficiente cargar una matriz con tipos de objeto y convertirla en alguna colección, después de cargar la matriz. Esto es especialmente cierto si la colección de destino no está indexada numéricamente.

Este algoritmo, exactamente como está escrito, creará una distribución muy uniforme donde no hay duplicados. Un aspecto MUY IMPORTANTE es que tiene que ser posible que la inserción del siguiente artículo se produzca hasta el tamaño actual + 1. Así, para el segundo artículo, podría ser posible almacenarlo en la ubicación 0 o la ubicación 1 Para el artículo 20, podría ser posible almacenarlo en cualquier ubicación, del 0 al 19. Es tan posible que el primer artículo permanezca en la ubicación 0 como que termine en cualquier otra ubicación. Es posible que el siguiente elemento nuevo vaya a cualquier parte, incluida la siguiente ubicación nueva.

La aleatoriedad de la secuencia será tan aleatoria como la aleatoriedad del generador de números aleatorios.

Este algoritmo también se puede utilizar para cargar tipos de referencia en ubicaciones aleatorias en una matriz. Dado que esto funciona con una matriz, también puede funcionar con colecciones. Eso significa que no tiene que crear la colección y luego mezclarla o ordenarla en el orden en que se inserten los objetos. La colección solo necesita tener la capacidad de insertar un elemento en cualquier lugar de la colección o adjuntarlo.

// RandomSequence.java
import java.util.Random;
public class RandomSequence {

    public static void main(String[] args) {
        // create an array of the size and type for which
        // you want a random sequence
        int[] randomSequence = new int[20];
        Random randomNumbers = new Random();

        for (int i = 0; i < randomSequence.length; i++ ) {
            if (i == 0) { // seed first entry in array with item 0
                randomSequence[i] = 0; 
            } else { // for all other items...
                // choose a random pointer to the segment of the
                // array already containing items
                int pointer = randomNumbers.nextInt(i + 1);
                randomSequence[i] = randomSequence[pointer]; 
                randomSequence[pointer] = i;
                // note that if pointer & i are equal
                // the new value will just go into location i and possibly stay there
                // this is VERY IMPORTANT to ensure the sequence is really random
                // and not biased
            } // end if...else
        } // end for
        for (int number: randomSequence) {
                System.out.printf("%2d ", number);
        } // end for
    } // end main
} // end class RandomSequence
Jim
fuente
0

Realmente todo depende exactamente para QUÉ necesitas la generación aleatoria, pero aquí está mi opinión.

Primero, cree un método independiente para generar el número aleatorio. Asegúrese de permitir límites.

public static int newRandom(int limit){
    return generatedRandom.nextInt(limit);  }

A continuación, querrá crear una estructura de decisión muy simple que compare valores. Esto se puede hacer de dos formas. Si tiene una cantidad muy limitada de números para verificar, una simple declaración IF será suficiente:

public static int testDuplicates(int int1, int int2, int int3, int int4, int int5){
    boolean loopFlag = true;
    while(loopFlag == true){
        if(int1 == int2 || int1 == int3 || int1 == int4 || int1 == int5 || int1 == 0){
            int1 = newRandom(75);
            loopFlag = true;    }
        else{
            loopFlag = false;   }}
    return int1;    }

Lo anterior compara int1 con int2 a int5, además de asegurarse de que no haya ceros en los randoms.

Con estos dos métodos implementados, podemos hacer lo siguiente:

    num1 = newRandom(limit1);
    num2 = newRandom(limit1);
    num3 = newRandom(limit1);
    num4 = newRandom(limit1);
    num5 = newRandom(limit1);

Seguido por:

        num1 = testDuplicates(num1, num2, num3, num4, num5);
        num2 = testDuplicates(num2, num1, num3, num4, num5);
        num3 = testDuplicates(num3, num1, num2, num4, num5);
        num4 = testDuplicates(num4, num1, num2, num3, num5);
        num5 = testDuplicates(num5, num1, num2, num3, num5);

Si tiene una lista más larga para verificar, entonces un método más complejo producirá mejores resultados tanto en la claridad del código como en los recursos de procesamiento.

Espero que esto ayude. Este sitio me ha ayudado tanto que me sentí en la obligación de al menos INTENTAR ayudar también.

AzerDraco
fuente
0

La forma más sencilla es utilizar nano DateTime como formato largo. System.nanoTime ();

linh đàm quang
fuente