Nos é dado um gerador de números aleatórios RandNum50
que gera um número inteiro aleatório uniformemente no intervalo de 1 a 50. Podemos usar apenas esse gerador de números aleatórios para gerar e imprimir todos os números inteiros de 1 a 100 em uma ordem aleatória. Todo número deve vir exatamente uma vez, e a probabilidade de qualquer número ocorrer em qualquer lugar deve ser igual.
Qual é o algoritmo mais eficiente para isso?
algorithms
integers
randomness
random-number-generator
Raj Wadhwa
fonte
fonte
RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1)
.Respostas:
Pensei (para que possa estar errado :-) nesta solução que usa o shuffle de Fisher-Yates . Para manter uma distribuição uniforme com boa aproximação (consulte a seção EDIT abaixo) a cada iteração, você pode usar este truque para produzir um valor entre e :0 k - 1O(N2) 0 k−1
krand
O algoritmo de Fisher-Yates se torna:
EDITAR:
Como apontado por Erick, am=⌈log2(k)⌉ r k
krand
função acima não retorna uma distribuição verdadeiramente uniforme. Existem outros métodos que podem ser usados para obter uma aproximação melhor (arbitrariamente melhor) e mais rápida; mas (até onde ), a única maneira de obter uma distribuição verdadeiramente uniforme é usar a amostragem por rejeição : escolha bits aleatórios e se o número obtido for menor que devolva-o, caso contrário, gere outro número aleatório; uma possível implementação:r kfonte
Como outras pessoas deram soluções aproximadas e envolvem a obtenção de números indeterminados de desvios, que tal uma prova de que não existe tal algoritmo que garanta apenas um número finito de
RandNum50()
chamadas?Como outros observaram, imprimir os números de 1 a 100 em ordem aleatória é equivalente a imprimir uma permutação aleatória desses números; existem 100! dessas permutações e, portanto, qualquer permutação específica deve ser gerada com probabilidade .1100!
Mas se soubéssemos que nosso algoritmo usava no máximo chamadas para alguns , poderíamos argumentar da seguinte maneira: primeiro, cubra os caminhos de computação que fazem menos de chamadas para fazer chamadas fictícias adicionais (ou seja, chamadas onde o valor retornado é irrelevante), de modo que todos os caminhos de computação fazem exatamente chamadas. Qualquer sequência de resultante de nossas chamadas para deve resultar em alguma permutação de saída e, portanto, podemos criar uma 'tabela de resultados' que mapeia qualquer sequência de resultados de nossas chamadas em um determinado permutação de saída. Como cada um desses resultados é igualmente provável (cada um deles tem probabilidadek k k k ( r 1 , r 2 , … , r k ) 1k k k k k (r1,r2,…,rk) c150k ), então a probabilidade de obter qualquer permutação específica de nosso algoritmo deve estar no formato para alguns . Mas Não pode ser desta forma, porquenão divide por nenhum (por exemplo, 3 divide mas não pode dividir nenhum número da forma ). Isso significa que nenhuma distribuição possível de resultados para chamadas com números aleatórios pode produzir uma permutação uniforme. c1c50k c 100! 50kk1100! 100! 50k k 50 k100! 50k
RandNum50
RandNum50
RandNum50
fonte
As soluções anteriores não são ótimas. A complexidade é exatamente nas chamadas para RandNum50 e é descrita em alguns detalhes aqui , usando como fonte de bit aleatório (como sugerido por Vor):nlogn+O(1)
A idéia básica é que você salve muitos bits se gerar um uniforme entre e, E, em seguida, utilizando a decomposição de base fatorial , em vez de gerar uma sequência de uniformes variou-se a , em seguida, , então , etc., . Este é realmente, como mencionei no post, o tópico de um artigo que enviei!n ! 1 2 3 n1 n! 1 2 3 n
Se você não souber como gerar um uniforme, como sugerido nesse post, a partir de um bit aleatório, também poderá gerar uma aproximação do uniforme diretamente, desta maneira (o que é equivalente ao "trulyrand" de Vor, mas mais rápido):
indo o mais longe que você precisa. Isso está desenvolvendo na base . Em seguida, basta truncar , ou seja, , no seu caso. Esse valor não é completamente aleatório, mas é uma medida de uniformidade que é frequentemente usada. Ou, como Vor sugere, você pode rejeitar se . Então, com esse valor, você pode fazer a expansão da base fatorial, conforme descrito na publicação .50 PP 50 P n = 100 ! P > nQ=Pmodn n=100! P>n
fonte
Eu não fiz a análise para confirmar o quão uniforme (ou não) isso seria, e poderia ser ajustado para ser um verdadeiro embaralhamento, mas você poderia escolher, a partir de uma matriz inicial do
i
th index =i + 1
, o(k + RandNum50() + RandNum50() - 1) mod (100 - k)
índice, com remoção, parak
= 0..99?Isso "empurra" o pico da
RandNum50() + RandNum50()
distribuição adiante uniformemente.Tenho certeza de que isso não está certo, como afirmei, porque o índice 0 (1) não pode ser obtido desde a primeira escolha e não consigo ver rapidamente um ajuste alternativo 1..50 + 1..50 que produz 0 ..99.
Atualizar
Para corrigir o problema que observei, usei efetivamente
RandNum100
conforme mencionado nos comentários da pergunta para inicializar aleatoriamente o primeirok
deslocamento.Isso produz uma distribuição com uma onda significativa na frente.
Em vez de avançar 1, usei outro
RandNum50
para incrementar isso primeirok
. Isso produz um resultado que é aleatório o suficiente para mim, mas ainda não é "verdadeiramente" aleatório, como pode ser facilmente visto se você alterar K para 2.Testando o código VB.NET, onde eu atendi qualquer K. par. Note que é O (K), 6K + 2, de fato.
fonte