Eu gostaria de gerar números aleatórios únicos entre 0 e 1000 que nunca se repetem (ou seja, 6 não aparecem duas vezes), mas isso não recorre a algo como uma pesquisa O (N) de valores anteriores para isso. Isso é possível?
algorithm
math
random
language-agnostic
dicroce
fonte
fonte
O(n)
tempo ou memória), muitas das respostas abaixo estão erradas, incluindo a resposta aceita.Respostas:
Inicialize uma matriz de 1001 números inteiros com os valores 0-1000 e defina uma variável, max, para o índice máximo atual da matriz (começando com 1000). Escolha um número aleatório, r, entre 0 e max, troque o número na posição r pelo número na posição max e retorne o número agora na posição max. Reduza no máximo 1 e continue. Quando max for 0, defina max novamente para o tamanho da matriz - 1 e inicie novamente sem a necessidade de reinicializar a matriz.
Atualização: Embora eu tenha inventado esse método sozinho quando respondi à pergunta, depois de algumas pesquisas, percebo que essa é uma versão modificada de Fisher-Yates conhecida como Durstenfeld-Fisher-Yates ou Knuth-Fisher-Yates. Como a descrição pode ser um pouco difícil de seguir, forneci um exemplo abaixo (usando 11 elementos em vez de 1001):
A matriz começa com 11 elementos inicializados na matriz [n] = n, max começa com 10:
A cada iteração, um número aleatório r é selecionado entre 0 e max, a matriz [r] e a matriz [max] são trocadas, a nova matriz [max] é retornada e max é decrementado:
Após 11 iterações, todos os números na matriz foram selecionados, max == 0, e os elementos da matriz são embaralhados:
Nesse ponto, o máximo pode ser redefinido para 10 e o processo pode continuar.
fonte
N
iterações (11 neste exemplo) para obter o resultado desejado cada vez significa que éO(n)
? Como você precisa fazerN
iterações para obterN!
combinações do mesmo estado inicial, caso contrário, sua saída será apenas um dos N estados.Você consegue fazer isso:
Portanto, isso não requer uma pesquisa de valores antigos a cada vez, mas ainda requer O (N) para o shuffle inicial. Mas, como Nils apontou nos comentários, isso é amortizado O (1).
fonte
Use um registro de deslocamento de feedback linear máximo .
É implementável em algumas linhas de C e em tempo de execução faz pouco mais do que alguns testes / ramificações, um pouco de adição e mudança de bits. Não é aleatório, mas engana a maioria das pessoas.
fonte
Você pode usar um gerador linear de congruência . Onde
m
(o módulo) seria o número primo mais próximo maior que 1000. Quando você obtém um número fora do intervalo, basta obter o próximo. A sequência será repetida apenas quando todos os elementos ocorrerem e você não precisar usar uma tabela. Esteja ciente das desvantagens deste gerador (incluindo falta de aleatoriedade).fonte
k
afastados do que na sequência nunca podem ocorrer juntos).Você pode usar a criptografia de preservação de formato para criptografar um contador. Seu contador passa de 0 para cima e a criptografia usa uma chave de sua escolha para transformá-la em um valor aparentemente aleatório de qualquer largura e largura desejada. Por exemplo, para o exemplo nesta pergunta: raiz 10, largura 3.
As cifras de bloco normalmente têm um tamanho fixo de bloco de, por exemplo, 64 ou 128 bits. Mas a Criptografia de Preservação de Formato permite que você pegue uma cifra padrão como AES e faça uma cifra de largura menor, de qualquer raio e largura que desejar, com um algoritmo ainda criptograficamente robusto.
É garantido que nunca haverá colisões (porque algoritmos criptográficos criam um mapeamento 1: 1). Também é reversível (um mapeamento bidirecional), para que você possa pegar o número resultante e voltar ao valor do contador que iniciou.
Essa técnica não precisa de memória para armazenar uma matriz aleatória, etc, o que pode ser uma vantagem em sistemas com memória limitada.
O AES-FFX é um método padrão proposto para conseguir isso. Eu experimentei algum código Python básico que é baseado na idéia do AES-FFX, embora não seja totalmente compatível - veja o código Python aqui . Pode, por exemplo, criptografar um contador para um número decimal de 7 dígitos com aparência aleatória ou um número de 16 bits. Aqui está um exemplo de raiz 10, largura 3 (para fornecer um número entre 0 e 999 inclusive), conforme a pergunta:
Para obter diferentes sequências pseudo-aleatórias e não repetidas, altere a chave de criptografia. Cada chave de criptografia produz uma sequência pseudo-aleatória não repetitiva diferente.
fonte
k
separados na sequência nunca podem ocorrer juntos).k
?1,2,...,N
por uma sequência dos mesmos números em alguma outra ordem, mas ainda constante. Os números são então retirados dessa sequência, um por um.k
é o número de valores escolhidos (o OP não especificou uma letra para isso, então tive que introduzir um).Para números baixos como 0 ... 1000, criar uma lista que contenha todos os números e embaralhar é direto. Mas se o conjunto de números para desenhar é muito grande, existe outra maneira elegante: você pode criar uma permutação pseudo-aleatória usando uma chave e uma função hash criptográfica. Veja o seguinte pseudocódigo de exemplo C ++ - ish:
Aqui
hash
estão apenas algumas funções pseudo-aleatórias arbitrárias que mapeiam uma cadeia de caracteres para um número inteiro possivelmente não assinado. A funçãorandperm
é uma permutação de todos os números dentro de 0 ... pow (2, bits) -1, assumindo uma chave fixa. Isso decorre da construção, porque cada passo que altera a variávelindex
é reversível. Isso é inspirado em uma cifra Feistel .fonte
hash()
, como usado no código acima, é uma função pseudo-aleatória segura, essa construção provará (Luby & Rackoff, 1988) produzir uma permutação pseudo - aleatória , que não pode ser distinguida de uma verdadeira aleatória aleatória usando significativamente menos esforço do que uma exaustiva pesquisa de todo o espaço da chave, que é exponencial no comprimento da chave. Mesmo para chaves de tamanho razoável (digamos, 128 bits), isso está além do poder total de computação disponível na Terra.hash( key + "/" + int2str(temp) )
construção ad hoc acima por HMAC , cuja segurança, por sua vez, pode ser comprovadamente reduzida à da função de compactação de hash subjacente. Além disso, o uso do HMAC pode tornar menos provável que alguém erroneamente tentar usar esta construção com uma função hash não-cripto inseguro).Você pode usar o meu algoritmo Xincrol descrito aqui:
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
Este é um método algorítmico puro de geração de números aleatórios, mas únicos, sem matrizes, listas, permutações ou carga de CPU pesada.
A versão mais recente também permite definir o intervalo de números. Por exemplo, se eu quiser números aleatórios exclusivos no intervalo de 0-1073741821.
Eu praticamente o usei para
É aberto, grátis. De uma chance...
fonte
k
separados na sequência nunca podem ocorrer juntos).Você nem precisa de um array para resolver este.
Você precisa de uma máscara de bits e um contador.
Inicialize o contador para zero e aumente-o em chamadas sucessivas. XOR o contador com a máscara de bits (selecionada aleatoriamente na inicialização ou corrigida) para gerar um número aleatório psu. Se você não pode ter números que excedam 1000, não use uma máscara de bits com mais de 9 bits. (Em outras palavras, a máscara de bits é um número inteiro não superior a 511.)
Certifique-se de que quando o contador ultrapassar 1000, você o zere. Nesse momento, você pode selecionar outra máscara de bits aleatória - se desejar - para produzir o mesmo conjunto de números em uma ordem diferente.
fonte
Eu acho que o gerador congruencial linear seria a solução mais simples.
e existem apenas três restrições à uma , c e m valores
PS: o método já foi mencionado, mas o post tem suposições erradas sobre os valores constantes. As constantes abaixo devem funcionar bem para o seu caso
No seu caso, você pode usar
a = 1002
,c = 757
,m = 1001
fonte
Aqui está um código que digitei que usa a lógica da primeira solução. Eu sei que isso é "independente de idioma", mas só queria apresentar isso como um exemplo em C #, caso alguém esteja procurando uma solução prática rápida.
fonte
Os resultados desse método são apropriados quando o limite é alto e você deseja gerar apenas alguns números aleatórios.
Observe que os números são gerados em ordem crescente, mas você pode embaralhar depois.
fonte
(top,n)=(100,10)
são:(0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635)
. Eu testei em Python, então pequenas diferenças na matemática podem ter um papel aqui (eu verifiquei que todas as operações de cálculor
são de ponto flutuante).Você pode usar um bom gerador de números pseudo-aleatórios com 10 bits e jogar fora de 1001 a 1023 deixando de 0 a 1000.
A partir daqui , obtemos o design para um PRNG de 10 bits.
10 bits, polinômio de realimentação x ^ 10 + x ^ 7 + 1 (período 1023)
use um GalFS LFSR para obter código rápido
fonte
N números aleatórios não repetidos terão complexidade O (n), conforme necessário.
Nota: Aleatório deve ser estático com a segurança da linha aplicada.
fonte
Digamos que você queira revisar as listas embaralhadas várias vezes, sem ter o
O(n)
atraso cada vez que você começar a embaralhá-las novamente, nesse caso, podemos fazer o seguinte:Crie 2 listas A e B, de 0 a 1000, ocupa
2n
espaço.A lista aleatória A usando Fisher-Yates leva
n
tempo.Ao desenhar um número, embarque Fisher-Yates em uma etapa na outra lista.
Quando o cursor estiver no final da lista, alterne para a outra lista.
Pré-processo
Desenhar
fonte
[1,3,4,5,2]
produzirá o mesmo resultado que a reprodução aleatória[1,2,3,4,5]
.A pergunta Como você gera com eficiência uma lista de K números inteiros que não se repetem entre 0 e um limite superior N é vinculada como duplicada - e se você deseja algo que seja O (1) por número aleatório gerado (sem O (n) custo de inicialização)), há um simples ajuste na resposta aceita.
Crie um mapa não ordenado vazio (um mapa ordenado vazio terá O (log k) por elemento) de inteiro para inteiro - em vez de usar uma matriz inicializada. Defina max como 1000 se esse for o máximo,
A única diferença em comparação com o uso de uma matriz inicializada é que a inicialização dos elementos é adiada / ignorada - mas gerará exatamente os mesmos números do mesmo PRNG.
fonte
Outra possibilidade:
Você pode usar uma matriz de sinalizadores. E pegue o próximo quando ele já estiver escolhido.
Porém, tenha cuidado após 1000 chamadas, a função nunca terminará; portanto, você deve fazer uma salvaguarda.
fonte
Aqui está um exemplo de código COBOL com o qual você pode brincar.
Posso enviar o arquivo RANDGEN.exe para que você possa brincar com ele para ver se ele deseja.
fonte
A maioria das respostas aqui não garante que elas não retornem o mesmo número duas vezes. Aqui está uma solução correta:
Não tenho certeza de que a restrição esteja bem especificada. Supõe-se que após 1000 outras saídas é permitido repetir um valor, mas que ingenuamente permite que 0 siga imediatamente após 0, desde que ambos apareçam no final e no início dos conjuntos de 1000. Por outro lado, enquanto é possível manter uma distância de Milhares de outros valores entre repetições, o que força uma situação em que a sequência se repete exatamente da mesma maneira todas as vezes, porque não há outro valor que tenha ocorrido fora desse limite.
Aqui está um método que sempre garante pelo menos 500 outros valores antes que um valor possa ser repetido:
fonte
Quando N for maior que 1000 e você precisar desenhar K amostras aleatórias, poderá usar um conjunto que contenha as amostras até o momento. Para cada sorteio, você usa amostragem por rejeição , que será uma operação "quase" O (1), portanto, o tempo total de execução é quase O (K) com armazenamento O (N).
Esse algoritmo entra em colisão quando K está "próximo" de N. Isso significa que o tempo de execução será muito pior que O (K). Uma correção simples é reverter a lógica para que, para K> N / 2, você mantenha um registro de todas as amostras que ainda não foram desenhadas. Cada sorteio remove uma amostra do conjunto de rejeição.
O outro problema óbvio com a amostragem por rejeição é que é o armazenamento de O (N), o que é uma má notícia se N estiver na casa dos bilhões ou mais. No entanto, existe um algoritmo que resolve esse problema. Este algoritmo é chamado algoritmo de Vitter depois de ser inventor. O algoritmo é descrito aqui . A essência do algoritmo de Vitter é que, após cada sorteio, você calcula um salto aleatório usando uma certa distribuição que garante amostragem uniforme.
fonte
Fisher Yates
Na verdade, é O (n-1), pois você só precisa de uma troca pelos dois últimos.
Isso é C #
fonte
Consulte minha resposta em https://stackoverflow.com/a/46807110/8794687
É um dos algoritmos mais simples que têm média complexidade de tempo O ( s log s ), s denotando o tamanho da amostra. Existem também alguns links para algoritmos de tabela de hash cuja complexidade é reivindicada como O ( s ).
fonte
Alguém postou "criando números aleatórios no excel". Eu estou usando esse ideal. Crie uma estrutura com 2 partes, str.index e str.ran; Para 10 números aleatórios, crie uma matriz de 10 estruturas. Defina o str.index de 0 a 9 e str.ran para um número aleatório diferente.
Classifique a matriz nos valores em arr [i] .ran. O str.index agora está em uma ordem aleatória. Abaixo está o código c:
fonte