Criação de números aleatórios sem duplicatas

87

Nesse caso, o MAX é de apenas 5, então pude verificar as duplicatas uma a uma, mas como fazer de forma mais simples? Por exemplo, e se MAX tiver um valor de 20? Obrigado.

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
fonte
3
Muitos geradores de números (pseudo) aleatórios não se repetem durante todo o seu "ciclo". O problema é, claro, que seu "ciclo" completo é de bilhões ou trilhões de valores, e os valores que eles produzem podem ser qualquer um desses bilhões ou trilhões de valores. Você poderia, em teoria, produzir um gerador de números aleatórios que tivesse um "ciclo" de 5 ou 10 ou qualquer outra coisa, mas provavelmente é mais problema do que vale a pena.
Hot Licks
1
Além disso, um gerador de números aleatórios que não se repete é ainda "menos" aleatório: se MAX = 5 e você ler 3 números, você pode adivinhar o próximo com 50% de probabilidade, se você ler 4 números, você sabe o próximo em 100% certo!
icza
Respondido a uma pergunta duplicada aqui
Alex - GlassEditor.com

Respostas:

149

A maneira mais simples seria criar uma lista dos números possíveis (1..20 ou qualquer outro) e depois embaralhá-los Collections.shuffle. Depois, pegue quantos elementos quiser. Isso é ótimo se o seu alcance for igual ao número de elementos de que você precisa no final (por exemplo, para embaralhar um baralho de cartas).

Isso não funciona tão bem se você quiser (digamos) 10 elementos aleatórios no intervalo de 1..10.000 - você acabaria fazendo muito trabalho desnecessariamente. Nesse ponto, provavelmente é melhor manter um conjunto de valores que você gerou até agora e apenas continuar gerando números em um loop até que o próximo ainda não esteja 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);
}

No entanto, tenha cuidado com a escolha do conjunto - usei deliberadamente, LinkedHashSetpois ele mantém a ordem de inserção, que nos interessa aqui.

Outra opção é sempre progredir, reduzindo o intervalo a cada vez e compensando os valores existentes. Então, por exemplo, suponha que você queira 3 valores no intervalo 0..9. Na primeira iteração, você geraria qualquer número no intervalo de 0 a 9 - digamos que você gerasse um 4.

Na segunda iteração, você geraria um número no intervalo 0..8. Se o número gerado for menor que 4, você o manterá como está ... caso contrário, você adicionará um a ele. Isso dá a você um intervalo de resultados de 0..9 sem 4. Suponha que obtivemos 7 dessa forma.

Na terceira iteração, você geraria um número no intervalo 0..7. Se o número gerado for menor que 4, você o manterá como está. Se for 4 ou 5, você adicionaria um. Se for 6 ou 7, você adicionaria dois. Dessa forma, o intervalo de resultados é 0..9 sem 4 ou 6.

Jon Skeet
fonte
Gere uma matriz dos valores possíveis, selecione aleatoriamente um (tamanho da matriz de mod de número aleatório), remova (e salve) o número selecionado e repita.
Hot Licks
Ou use um gerador aleatório com um ciclo completo (aqueles baseados em primos podem usar pequenos primos - com pequenos ciclos correspondentes) e eliminar valores fora do intervalo.
Paul de Vrieze,
A solução "Outra opção é sempre progredir" é WAAAAY melhor. Por favor, edite para refletir. E obrigado por esta resposta incrível.
user123321
1
@musselwhizzle: Tentará encontrar tempo em breve. Eu não tenho certeza sobre "WAAAY melhor" - vai ser significativamente menos "obviamente correto", embora seja mais eficiente. Muitas vezes fico feliz em sacrificar o desempenho por uma questão de legibilidade.
Jon Skeet
@Deepthi: Qualquer máximo que o OP deseja - conforme a pergunta.
Jon Skeet,
19

É assim que eu faria

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 o estimado Sr. Skeet apontou:
Se n é o número de números selecionados aleatoriamente que você deseja escolher e N é o espaço amostral total de números disponíveis para seleção:

  1. Se n << N , você deve apenas armazenar os números que escolheu e verificar uma lista para ver se o número selecionado está nela.
  2. Se n ~ = N , você provavelmente deve usar meu método, preenchendo uma lista contendo todo o espaço amostral e removendo os números ao selecioná-los.
Catchwa
fonte
lista deve ser uma LinkedList, remover índices aleatórios de arraylist é muito ineficiente
Riccardo Casatta
@RiccardoCasatta você tem uma fonte para sua afirmação? Não posso imaginar que percorrer uma lista vinculada seria muito bom também. Veja também: stackoverflow.com/a/6103075/79450
Catchwa
Eu testei e você está certo, devo deletar meu comentário?
Riccardo Casatta
@RiccardoCasatta Outros podem achar nossas idas e vindas úteis
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
fonte
5

Há outra maneira de fazer números ordenados "aleatórios" com LFSR, dê uma olhada em:

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

com essa técnica você pode obter o número aleatório ordenado por índice e certificando-se de que os valores não sejam duplicados.

Mas esses não são números aleatórios VERDADEIROS porque a geração aleatória é determinística.

Mas, dependendo do seu caso, você pode usar essa técnica reduzindo a quantidade de processamento na geração de números aleatórios ao usar o embaralhamento.

Aqui está um algoritmo LFSR em java, (eu o levei em algum lugar que não me lembro):

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
fonte
4

Outra abordagem que lhe permite especificar com quantos números deseja sizee os valores mine maxdos números devolvidos

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 usá-lo retornando 7 números entre 0 e 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
fonte
4

Isso seria muito mais simples em java-8:

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

A maneira mais eficiente e básica de ter números aleatórios não repetidos é explicada por este pseudocódigo. Não é necessário ter loops aninhados ou pesquisas com 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
}

Suponha que a primeira iteração gerou um número aleatório 3 para começar (de 0 a 19). Isso tornaria os resultados [0] = mapeamento [3], ou seja, o valor 3. Em seguida, atribuiríamos mapeamento [3] a 19.

Na próxima iteração, o número aleatório era 5 (de 0 a 18). Isso tornaria os resultados [1] = mapeamento [5], ou seja, o valor 5. Em seguida, atribuiríamos mapeamento [5] a 18.

Agora suponha que a próxima iteração escolha 3 novamente (de 0 a 17). resultados [2] seriam atribuídos ao valor de mapeamento [3], mas agora, esse valor não é 3, mas 19.

Essa mesma proteção persiste para todos os números, mesmo se você obtiver o mesmo número 5 vezes consecutivas. Por exemplo, se o gerador de números aleatórios fornecesse 0 cinco vezes seguidas, os resultados seriam: [0, 19, 18, 17, 16].

Você nunca obteria o mesmo número duas vezes.

Blackcatweb
fonte
Duvido que isso seja tão aleatório quanto você faz parecer. Ele passa nos testes de aleatoriedade padrão ?; pareceria concentrar números próximos ao fim do espectro.
tucuxi
Aqui está um caso básico. O pool é {a, b, c}. Precisamos de 2 elementos não repetitivos. Seguindo o algoritmo, aqui estão as combinações que poderíamos desenhar e seus 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 Pontuação: a-4, b-4, c-4
blackcatweb
3

Gerar todos os índices de uma sequência geralmente é uma má ideia, pois pode levar muito tempo, especialmente se a proporção dos números a serem escolhidos MAXfor baixa (a complexidade é dominada por O(MAX)). Isso fica pior se a proporção dos números a serem escolhidos se MAXaproximar de um, pois então remover os índices escolhidos da sequência de todos também se torna caro (nos aproximamos O(MAX^2/2)). Mas para números pequenos, isso geralmente funciona bem e não é particularmente sujeito a erros.

Filtrar os índices gerados usando uma coleção também é uma má ideia, pois algum tempo é gasto na inserção dos índices na sequência, e o progresso não é garantido, pois o mesmo número aleatório pode ser desenhado várias vezes (mas para grande o suficiente MAXé improvável ) Isso pode ser quase complexo
O(k n log^2(n)/2), ignorando as duplicatas e assumindo que a coleção usa uma árvore para uma pesquisa eficiente (mas com um custo constante significativo kde alocação dos nós da árvore e possivelmente tendo que ser rebalanceado ).

Outra opção é gerar os valores aleatórios exclusivamente desde o início, garantindo que o progresso esteja sendo feito. Isso significa que na primeira rodada, um índice aleatório [0, MAX]é gerado:

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

Na segunda rodada, apenas [0, MAX - 1]é gerado (pois um item já foi selecionado):

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)

Os valores dos índices precisam então ser ajustados: se o segundo índice cair na segunda metade da sequência (após o primeiro índice), ele precisa ser incrementado para compensar a lacuna. Podemos implementar isso como um loop, permitindo-nos selecionar um número arbitrário de itens exclusivos.

Para sequências curtas, este é um 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
}

Onde n_select_numestá o seu 5 e n_number_numé o seu MAX. O n_Rand(x)retorna números inteiros aleatórios em [0, x](inclusive). Isso pode ser um pouco mais rápido ao selecionar muitos itens (por exemplo, não 5, mas 500) usando a pesquisa binária para encontrar o ponto de inserção. Para fazer isso, precisamos nos certificar de que atendemos aos requisitos.

Faremos uma pesquisa binária com a comparação n + j < rand_num[j]que é a mesma que
n < rand_num[j] - j. Precisamos mostrar que rand_num[j] - jainda é uma seqüência classificada para uma seqüência classificada rand_num[j]. Felizmente, isso é facilmente mostrado, pois a distância mais baixa entre dois elementos do original rand_numé um (os números gerados são únicos, portanto, sempre há uma diferença de pelo menos 1). Ao mesmo tempo, se subtrairmos os índices jde todos os elementos
rand_num[j], as diferenças no índice serão exatamente 1. Portanto, no "pior" caso, obtemos uma sequência constante - mas nunca decrescente. A pesquisa binária pode, portanto, ser usada, produzindo o 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
    }
};

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

Eu testei isso em três benchmarks. Primeiro, 3 números foram escolhidos de 7 itens, e um histograma dos itens escolhidos foi acumulado em 10.000 execuções:

4265 4229 4351 4267 4267 4364 4257

Isso mostra que cada um dos 7 itens foi escolhido aproximadamente o mesmo número de vezes, e não há viés aparente causado pelo algoritmo. Todas as sequências também foram verificadas quanto à exatidão (unicidade de conteúdo).

O segundo benchmark envolveu a escolha de 7 números de 5000 itens. O tempo de várias versões do algoritmo foi acumulado em 10.000.000 execuções. Os resultados são indicados nos comentários no código como b1. A versão simples do algoritmo é um pouco mais rápida.

O terceiro benchmark envolveu a escolha de 700 números entre 5000 itens. O tempo de várias versões do algoritmo foi novamente acumulado, desta vez mais de 10.000 execuções. Os resultados são indicados nos comentários no código como b2. A versão de pesquisa binária do algoritmo é agora mais de duas vezes mais rápida do que a simples.

O segundo método começa a ser mais rápido para escolher mais do que cerca de 75 itens em minha máquina (observe que a complexidade de qualquer um dos algoritmos não depende do número de itens MAX).

Vale ressaltar que os algoritmos acima geram os números aleatórios em ordem crescente. Mas seria simples adicionar outra matriz na qual os números seriam salvos na ordem em que foram gerados e retornar isso (a um custo adicional insignificante O(n)). Não é necessário embaralhar a saída: isso seria muito mais lento.

Observe que os fontes estão em C ++, não tenho Java na minha máquina, mas o conceito deve estar claro.

EDITAR :

Por diversão, também implementei a abordagem que gera uma lista com todos os índices
0 .. MAX, os escolhe aleatoriamente e os remove da lista para garantir a exclusividade. Como escolhi bastante alto MAX(5000), o desempenho é 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

Também implementei a abordagem com a set(uma coleção C ++), que na verdade vem em segundo lugar no benchmark b2, sendo apenas cerca de 50% mais lenta do que a abordagem com a pesquisa binária. Isso é compreensível, pois o setutiliza uma árvore binária, onde o custo de inserção é semelhante ao da busca binária. A única diferença é a chance de obter itens duplicados, o que retarda o progresso.

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

O código-fonte completo está aqui .

o porco
fonte
2

Você pode usar uma das classes que implementam a interface Set ( API ) e, em seguida, cada número gerado, usar Set.add () para inseri-lo.

Se o valor de retorno for falso, você sabe que o número já foi gerado antes.

SSTwinrova
fonte
2

Em vez de fazer tudo isso, crie um LinkedHashSetobjeto e números aleatórios para ele por Math.random()função ... se qualquer entrada duplicada ocorrer, o LinkedHashSetobjeto não adicionará esse número à sua Lista ... Visto que nesta Classe de Coleção não são permitidos valores duplicados. no final, você obtém uma lista de números aleatórios sem valores duplicados ....: D

Abdeali Chandan
fonte
2

Seu problema parece reduzir ao escolher k elementos aleatoriamente de uma coleção de n elementos. A resposta de Collections.shuffle é, portanto, correta, mas conforme apontado ineficiente: é O (n).

Wikipedia: Fisher – Yates shuffle tem uma versão O (k) quando o array já existe. No seu caso, não há array de elementos e criar o array de elementos pode ser muito caro, digamos se max fosse 10000000 em vez de 20.

O algoritmo de embaralhamento envolve a inicialização de um array de tamanho n onde cada elemento é igual ao seu índice, pegando k números aleatórios de cada número em um intervalo com o máximo um menor que o intervalo anterior e, em seguida, trocando elementos no final do array.

Você pode fazer a mesma operação no tempo O (k) com um hashmap, embora eu admita que isso é um problema. Observe que isso só vale a pena se k for muito menor que n. (ou seja, k ~ lg (n) ou mais), caso contrário, você deve usar o shuffle diretamente.

Você usará seu hashmap como uma representação eficiente da matriz de apoio no algoritmo de embaralhamento. Qualquer elemento da matriz que seja igual ao seu índice não precisa aparecer no mapa. Isso permite que você represente um array de tamanho n em tempo constante, não há tempo gasto para inicializá-lo.

  1. Escolha k números aleatórios: o primeiro está no intervalo de 0 a n-1, o segundo 0 a n-2, o terceiro 0 a n-3 e assim por diante, até nk.

  2. Trate seus números aleatórios como um conjunto de trocas. O primeiro índice aleatório muda para a posição final. O segundo índice aleatório muda para a penúltima posição. No entanto, em vez de trabalhar com uma matriz de apoio, trabalhe com seu hashmap. Seu hashmap armazenará todos os itens que estiverem fora de posição.

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
fonte
creating the array of elements could be very expensive- por que criar um array seria mais caro do que embaralhar? Eu acho que não há absolutamente nenhuma razão para pessimismo neste ponto :-)
Wolf
1

O código a seguir cria um número aleatório de sequência entre [1, m] que não foi gerado 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
fonte
0

Existe um algoritmo de lote de cartão: você cria uma matriz ordenada de números (o "lote de cartão") e em cada iteração você seleciona um número em uma posição aleatória (removendo o número selecionado do "lote de cartão", é claro).

Lavir o Whiolet
fonte
0

Aqui está uma solução eficiente para a criação rápida de uma matriz aleatória. Após a randomização, você pode simplesmente escolher o n-ésimo elemento eda matriz, incrementar ne retornar e. Esta solução tem O (1) para obter um número aleatório e O (n) para a inicialização, mas como compensação requer uma boa quantidade de memória se n for grande o suficiente.

Martin Thurau
fonte
0

Há uma solução mais eficiente e menos complicada para inteiros do que Collections.shuffle.

O problema é o mesmo que escolher itens sucessivamente apenas dos itens não coletados em um conjunto e colocá-los em ordem em outro lugar. Isso é exatamente como distribuir cartas aleatoriamente ou sacar bilhetes de rifa vencedores de um chapéu ou caixa.

Este algoritmo funciona para carregar qualquer array e atingir uma ordem aleatória no final do carregamento. Ele também funciona para adicionar em uma coleção List (ou qualquer outra coleção indexada) e alcançar uma sequência aleatória na coleção no final das adições.

Isso pode ser feito com uma única matriz, criada uma vez, ou um conjunto ordenado numericamente, como uma Lista, no local. Para uma matriz, o tamanho inicial da matriz precisa ser o tamanho exato para conter todos os valores pretendidos. Se você não souber quantos valores podem ocorrer de antemão, usar uma coleção numericamente ordenada, como ArrayList ou List, em que o tamanho não é imutável, também funcionará. Ele funcionará universalmente para uma matriz de qualquer tamanho até Integer.MAX_VALUE, que é pouco mais de 2.000.000.000. Os objetos de lista terão os mesmos limites de índice. Sua máquina pode ficar sem memória antes de você obter um array desse tamanho. Pode ser mais eficiente carregar uma matriz digitada para os tipos de objeto e convertê-la em alguma coleção, após carregar a matriz. Isso é especialmente verdadeiro se a coleção de destino não for indexada numericamente.

Este algoritmo, exatamente como escrito, criará uma distribuição muito uniforme onde não há duplicatas. Um aspecto MUITO IMPORTANTE é que deve ser possível que a inserção do próximo item ocorra até o tamanho atual + 1. Assim, para o segundo item, poderá ser possível armazená-lo na localização 0 ou localização 1 Para o 20º item, pode ser possível armazená-lo em qualquer local, de 0 a 19. É tão possível que o primeiro item fique no local 0, pois pode acabar em qualquer outro local. É igualmente possível que o próximo novo item vá a qualquer lugar, incluindo o próximo novo local.

A aleatoriedade da sequência será tão aleatória quanto a aleatoriedade do gerador de números aleatórios.

Este algoritmo também pode ser usado para carregar tipos de referência em locais aleatórios em uma matriz. Como isso funciona com uma matriz, também pode funcionar com coleções. Isso significa que você não precisa criar a coleção e, em seguida, embaralhá-la ou ordená-la nas ordens em que os objetos estão sendo inseridos. A coleção só precisa ter a capacidade de inserir um item em qualquer lugar da coleção ou anexá-lo.

// 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
fonte
0

Realmente tudo depende exatamente do QUE você precisa para a geração aleatória, mas aqui está a minha opinião.

Primeiro, crie um método autônomo para gerar o número aleatório. Certifique-se de permitir limites.

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

Em seguida, você desejará criar uma estrutura de decisão muito simples que compare valores. Isso pode ser feito de um desses dois jeitos. Se você tiver uma quantidade muito limitada de números para verificar, uma simples declaração 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;    }

O procedimento acima compara int1 a int2 a int5, além de garantir que não haja zeros nos randoms.

Com esses dois métodos em vigor, podemos fazer o seguinte:

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

Se você tiver uma lista mais longa para verificar, um método mais complexo produzirá melhores resultados tanto em clareza de código quanto em recursos de processamento.

Espero que isto ajude. Este site tem me ajudado muito, me senti na obrigação de pelo menos TENTAR ajudar também.

AzerDraco
fonte
0

A maneira mais fácil é usar o nano DateTime como formato longo. System.nanoTime ();

Linh đàm Quang
fonte