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;
}
}
Respostas:
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:
No entanto, tenha cuidado com a escolha do conjunto - usei deliberadamente,
LinkedHashSet
pois 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.
fonte
É assim que eu faria
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:
fonte
fonte
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):
fonte
Outra abordagem que lhe permite especificar com quantos números deseja
size
e os valoresmin
emax
dos números devolvidosPara usá-lo retornando 7 números entre 0 e 25.
fonte
Isso seria muito mais simples em
java-8
:fonte
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:
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.
fonte
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
MAX
for baixa (a complexidade é dominada porO(MAX)
). Isso fica pior se a proporção dos números a serem escolhidos seMAX
aproximar de um, pois então remover os índices escolhidos da sequência de todos também se torna caro (nos aproximamosO(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 complexoO(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 significativok
de 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:Na segunda rodada, apenas
[0, MAX - 1]
é gerado (pois um item já foi selecionado):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 :Onde
n_select_num
está o seu 5 en_number_num
é o seuMAX
. On_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 quen < rand_num[j] - j
. Precisamos mostrar querand_num[j] - j
ainda é uma seqüência classificada para uma seqüência classificadarand_num[j]
. Felizmente, isso é facilmente mostrado, pois a distância mais baixa entre dois elementos do originalrand_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 índicesj
de todos os elementosrand_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 oO(n log(n))
algoritmo:E finalmente:
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:
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 altoMAX
(5000), o desempenho é catastrófico:Também implementei a abordagem com a
set
(uma coleção C ++), que na verdade vem em segundo lugar no benchmarkb2
, sendo apenas cerca de 50% mais lenta do que a abordagem com a pesquisa binária. Isso é compreensível, pois oset
utiliza 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.O código-fonte completo está aqui .
fonte
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.
fonte
Em vez de fazer tudo isso, crie um
LinkedHashSet
objeto e números aleatórios para ele porMath.random()
função ... se qualquer entrada duplicada ocorrer, oLinkedHashSet
objeto 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 ....: Dfonte
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.
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.
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.
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 :-)O código a seguir cria um número aleatório de sequência entre [1, m] que não foi gerado antes.
fonte
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).
fonte
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 elementoe
da matriz, incrementarn
e retornare
. 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.fonte
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.
fonte
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.
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:
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:
Seguido por:
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.
fonte
Eu criei um snippet que não gera nenhum inteiro aleatório duplicado. a vantagem desse snippet é que você pode atribuir a lista de um array a ele e gerar o item aleatório também.
Nenhuma classe de gerador aleatório de duplicação
fonte
A maneira mais fácil é usar o nano DateTime como formato longo. System.nanoTime ();
fonte