Dada uma função que produz um número inteiro aleatório no intervalo de 1 a 5, escreva uma função que produz um número inteiro aleatório no intervalo de 1 a 7.
- O que é uma solução simples?
- O que é uma solução eficaz para reduzir o uso de memória ou executar em uma CPU mais lenta?
7 * rand5() / 5
?Respostas:
Isso é equivalente à solução de Adam Rosenfield, mas pode ser um pouco mais claro para alguns leitores. Ele assume que rand5 () é uma função que retorna um número inteiro estatisticamente aleatório no intervalo de 1 a 5, inclusive.
Como funciona? Pense assim: imagine imprimir esse conjunto de dupla dimensão no papel, prendendo-o em um cartão de dardo e jogando dardos aleatoriamente nele. Se você atingir um valor diferente de zero, é um valor estatisticamente aleatório entre 1 e 7, pois há um número igual de valores diferentes de zero para escolher. Se você acertar um zero, continue jogando o dardo até atingir um diferente de zero. É isso que esse código está fazendo: os índices iej selecionam aleatoriamente um local no quadro de dardos e, se não obtivermos um bom resultado, continuamos jogando dardos.
Como Adam disse, isso pode durar para sempre no pior caso, mas estatisticamente o pior caso nunca acontece. :)
fonte
rand5
for uniforme, todas as células davals
grade têm a mesma probabilidade de serem selecionadas. A grade contém exatamente três cópias de cada número inteiro no intervalo [1, 7], mais quatro zeros. Portanto, o fluxo "bruto" de resultados tende a uma mistura uniforme de [1, 7] valores, mais alguns zeros que ocorrem um pouco mais frequentemente do que qualquer valor permitido individual. Mas isso não importa, porque os zeros são eliminados, deixando apenas uma mistura uniforme de [1, 7] valores.Não existe uma solução (exatamente correta) que funcione em uma quantidade constante de tempo, já que 1/7 é um decimal infinito na base 5. Uma solução simples seria usar a amostragem por rejeição, por exemplo:
Isso tem um tempo de execução esperado de 25/21 = 1,19 iterações do loop, mas há uma probabilidade infinitesimalmente pequena de loop para sempre.
fonte
N
chamadasrand5()
no pior dos casos. Em seguida, existem 5 ^ N resultados possíveis da sequência de chamadas pararand5
, cada uma com uma saída de 1-7. Portanto, se você adicionar todas as sequências possíveis de chamadas cuja saída ék
para cada 1≤k≤7, a probabilidade de que a saída sejak
é m / 5 ^ N, onde m é o número dessas seqüências. Portanto, m / 5 ^ N = 1/7, mas não há soluções inteiras possíveis (N, m) para essa ==> contradição.Gostaria de adicionar outra resposta, além da minha primeira resposta . Essa resposta tenta minimizar o número de chamadas
rand5()
por chamadarand7()
para maximizar o uso da aleatoriedade. Ou seja, se você considera a aleatoriedade um recurso precioso, queremos usar o máximo possível, sem jogar fora nenhum bit aleatório. Essa resposta também tem algumas semelhanças com a lógica apresentada na resposta de Ivan .A entropia de uma variável aleatória é uma quantidade bem definida. Para uma variável aleatória que assume N estados com probabilidades iguais (uma distribuição uniforme), a entropia é log 2 N. Assim,
rand5()
possui aproximadamente 2,332193 bits de entropia erand7()
cerca de 2,80735 bits de entropia. Se esperamos maximizar nosso uso da aleatoriedade, precisamos usar todos os 2,332193 bits de entropia de cada chamada pararand5()
e aplicá-los na geração de 2,80735 bits de entropia necessários para cada chamadarand7()
. O limite fundamental, então, é que não podemos fazer melhor do que log (7) / log (5) = 1,20906 chamadas pararand5()
por chamadarand7()
.Notas laterais: todos os logaritmos nesta resposta serão da base 2, a menos que seja especificado o contrário.
rand5()
será assumido como retornando números no intervalo [0, 4] erand7()
assumido como retornando números no intervalo [0, 6]. Ajustar os intervalos para [1, 5] e [1, 7] respectivamente é trivial.Então, como fazemos isso? Geramos um número real aleatório infinitamente preciso entre 0 e 1 (finja no momento que poderíamos realmente computar e armazenar um número infinitamente preciso - resolveremos isso mais tarde). Podemos gerar esse número gerando seus dígitos na base 5: escolhemos o número aleatório 0.
a
1a
2a
3 ..., em que cada dígito ai
é escolhido por uma chamada pararand5()
. Por exemplo, se nosso RNG escolheu ai
= 1 para todosi
, ignorando o fato de que isso não é muito aleatório, isso corresponderia ao número real 1/5 + 1/5 2 + 1/5 3 + ... = 1/4 (soma de uma série geométrica).Ok, escolhemos um número real aleatório entre 0 e 1. Agora, afirmo que esse número aleatório é distribuído uniformemente. Intuitivamente, isso é fácil de entender, já que cada dígito foi escolhido de maneira uniforme e o número é infinitamente preciso. No entanto, uma prova formal disso é um pouco mais envolvida, já que agora estamos lidando com uma distribuição contínua em vez de uma distribuição discreta, por isso precisamos provar que a probabilidade de que nosso número esteja em um intervalo [
a
,b
] é igual à duração de esse intervalob - a
,. A prova é deixada como um exercício para o leitor =).Agora que temos um número real aleatório selecionado uniformemente no intervalo [0, 1], precisamos convertê-lo em uma série de números aleatórios uniformemente no intervalo [0, 6] para gerar a saída de
rand7()
. Como vamos fazer isso? Exatamente o inverso do que acabamos de fazer - nós o convertemos em um decimal infinitamente preciso na base 7, e então cada dígito da base 7 corresponderá a uma saída derand7()
.Tomando o exemplo anterior, se
rand5()
produz um fluxo infinito de 1's, então nosso número real aleatório será 1/4. Convertendo 1/4 para a base 7, obtemos o decimal infinito 0,15151515 ..., portanto, produziremos como saída 1, 5, 1, 5, 1, 5, etc.Ok, então temos a idéia principal, mas ainda temos dois problemas: não podemos computar ou armazenar um número real infinitamente preciso; então, como lidamos com apenas uma parte finita dele? Em segundo lugar, como realmente o convertemos para a base 7?
Uma maneira de converter um número entre 0 e 1 na base 7 é a seguinte:
Para lidar com o problema da precisão infinita, calculamos um resultado parcial e também armazenamos um limite superior sobre o que poderia ser o resultado. Ou seja, suponha que tenhamos chamado
rand5()
duas vezes e retornou 1 nas duas vezes. O número que geramos até agora é 0,11 (base 5). Qualquer que seja o restante da série infinita de chamadas arand5()
produzir, o número real aleatório que estamos gerando nunca será maior que 0,12: é sempre verdade que 0,11 ≤ 0,11xyz ... <0,12.Portanto, acompanhando o número atual até o momento e o valor máximo que ele poderia ter, convertemos os dois números na base 7. Se eles concordarem com os primeiros
k
dígitos, podemos gerar com segurança os próximosk
dígitos - independentemente do que fluxo infinito de 5 dígitos da base, eles nunca afetarão a próximak
dígitos da representação da base 7!E esse é o algoritmo - para gerar a próxima saída de
rand7()
, geramos apenas quantos dígitosrand5()
precisamos para garantir que sabemos com certeza o valor do próximo dígito na conversão do número real aleatório em base 7. Aqui está uma implementação Python, com um equipamento de teste:Observe que
rand7_gen()
retorna um gerador, pois possui um estado interno que envolve a conversão do número em base 7. O chicote de teste chamanext(r7)
10000 vezes para produzir 10000 números aleatórios e mede sua distribuição. Somente matemática inteira é usada, portanto os resultados estão exatamente corretos.Observe também que os números aqui ficam muito grandes, muito rápidos. Poderes de 5 e 7 crescem rapidamente. Portanto, o desempenho começará a diminuir visivelmente após a geração de muitos números aleatórios, devido à aritmética do bignum. Mas lembre-se aqui, meu objetivo era maximizar o uso de bits aleatórios, não maximizar o desempenho (embora esse seja um objetivo secundário).
Em uma execução, fiz 12091 chamadas
rand5()
para 10000 chamadas pararand7()
, atingindo o mínimo de chamadas log (7) / log (5) em média para 4 números significativos, e a saída resultante foi uniforme.Para portar esse código para um idioma que não tenha inteiros arbitrariamente grandes incorporados, você deverá limitar os valores
pow5
epow7
o valor máximo do seu tipo integral nativo - se eles ficarem muito grandes, redefina tudo e começar de novo. Isso aumentará o número médio de chamadasrand5()
por chamada pararand7()
um pouco, mas espero que não aumente muito, mesmo para números inteiros de 32 ou 64 bits.fonte
(Eu roubei a resposta de Adam Rosenfeld e a fiz rodar cerca de 7% mais rápido.)
Suponha que rand5 () retorne um de {0,1,2,3,4} com distribuição igual e a meta seja retornar {0,1,2,3,4,5,6} com distribuição igual.
Estamos acompanhando o maior valor que o loop pode gerar na variável
max
. Se o resultado até agora estiver entre max% 7 e max-1, o resultado será distribuído uniformemente nesse intervalo. Caso contrário, usamos o restante, que é aleatório entre 0 e max% 7-1, e outra chamada para rand () para criar um novo número e um novo máximo. Então começamos novamente.Edit: Espere o número de vezes para chamar rand5 () é x nesta equação:
fonte
5 * rand5() + rand5()
.Algoritmo:
7 pode ser representado em uma sequência de 3 bits
Use rand (5) para preencher aleatoriamente cada bit com 0 ou 1.
Por exemplo: chame rand (5) e
se o resultado for 1 ou 2, preencha o bit com 0
se o resultado for 4 ou 5, preencha o bit com 1
se o resultado for 3, então ignore e faça novamente (rejeição)
Dessa forma, podemos preencher 3 bits aleatoriamente com 0/1 e, assim, obter um número de 1 a 7.
EDIT: Esta parece ser a resposta mais simples e eficiente, então aqui está um código:
fonte
fonte
Edit: Isso não funciona muito bem. É desligado em cerca de 2 partes em 1000 (assumindo um rand5 perfeito). Os baldes recebem:
Ao mudar para uma soma de
parece ganhar uma ordem de magnitude para cada 2 adicionados
BTW: a tabela de erros acima não foi gerada por amostragem, mas pela seguinte relação de recorrência:
fonte
fonte
ans += (r < 3) << i
A seguir, produz uma distribuição uniforme em {1, 2, 3, 4, 5, 6, 7} usando um gerador de números aleatórios produzindo uma distribuição uniforme em {1, 2, 3, 4, 5}. O código é confuso, mas a lógica é clara.
fonte
Diferentemente da solução escolhida, o algoritmo será executado em tempo constante. No entanto, faz mais 2 chamadas para rand5 do que o tempo médio de execução da solução escolhida.
Observe que este gerador não é perfeito (o número 0 tem 0,0064% mais chances do que qualquer outro número), mas, para fins mais práticos, a garantia de tempo constante provavelmente supera essa imprecisão.
Explicação
Essa solução é derivada do fato de que o número 15.624 é divisível por 7 e, portanto, se podemos gerar aleatoriamente e uniformemente números de 0 a 15.624 e, em seguida, usar o mod 7, podemos obter um gerador rand7 quase uniforme. Os números de 0 a 15.624 podem ser gerados uniformemente rolando rand5 6 vezes e usando-os para formar os dígitos de um número base 5 da seguinte maneira:
As propriedades do mod 7, no entanto, permitem simplificar um pouco a equação:
assim
torna-se
Teoria
O número 15.624 não foi escolhido aleatoriamente, mas pode ser descoberto usando o pequeno teorema de Fermat, que afirma que se p é um número primo, então
Então isso nos dá,
(5 ^ 6) -1 é igual a
Este é um número na forma de base 5 e, portanto, podemos ver que esse método pode ser usado para ir de qualquer gerador de números aleatórios para qualquer outro gerador de números aleatórios. Embora um pequeno desvio para 0 seja sempre introduzido ao usar o expoente p-1.
Para generalizar essa abordagem e para ser mais preciso, podemos ter uma função como esta:
fonte
Os problemas de lição de casa são permitidos aqui?
Essa função faz cálculos brutos da "base 5" para gerar um número entre 0 e 6.
fonte
Se considerarmos a restrição adicional de tentar dar a resposta mais eficiente, ou seja, uma que forneceu um fluxo de entrada
I
, de números inteiros uniformemente distribuídosm
de 1 a 5 produz um fluxoO
, de números inteiros distribuídos uniformemente de 1 a 7 do comprimento mais longo param
, digamosL(m)
.A maneira mais simples de analisar isso é tratar os fluxos I e
O
como números de 5 e 7 anos, respectivamente. Isso é alcançado pela idéia da resposta principal de pegar o fluxoa1, a2, a3,... -> a1+5*a2+5^2*a3+..
e da mesma forma para o fluxoO
.Então, se fizermos uma seção do fluxo de entrada de comprimento
m choose n s.t. 5^m-7^n=c
ondec>0
e for o menor possível. Depois, há um mapa uniforme do fluxo de entrada de comprimento m para números inteiros de1
para5^m
e outro mapa uniforme de números inteiros de 17^n
para o fluxo de saída de comprimento n onde podemos ter que perder alguns casos do fluxo de entrada quando o número inteiro mapeado excede7^n
.Portanto, isso fornece um valor para
L(m)
de em torno dom (log5/log7)
qual é aproximadamente.82m
.A dificuldade com a análise acima é a equação
5^m-7^n=c
que não é fácil de resolver exatamente e o caso em que o valor uniforme de1
para5^m
excede7^n
e perdemos eficiência.A questão é quão próximo do melhor valor possível de m (log5 / log7) pode ser alcançado. Por exemplo, quando esse número se aproxima de um número inteiro, podemos encontrar uma maneira de atingir esse número inteiro exato de valores de saída?
Se
5^m-7^n=c
, a partir do fluxo de entrada, geramos efetivamente um número aleatório uniforme de0
para(5^m)-1
e não usamos valores maiores que7^n
. No entanto, esses valores podem ser resgatados e usados novamente. Eles efetivamente geram uma seqüência uniforme de números de 1 a5^m-7^n
. Assim, podemos tentar usá-los e convertê-los em números de 7 anos, para que possamos criar mais valores de saída.Se deixarmos
T7(X)
ser o comprimento médio da sequência de saída derandom(1-7)
números inteiros derivada de uma entrada uniforme de tamanhoX
, e assumindo isso5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7
.Então,
T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)
como temos um comprimento sem sequência com probabilidade 7 ^ n0 / 5 ^ m com um resíduo de comprimento5^m-7^n0
com probabilidade(5^m-7^n0)/5^m)
.Se continuarmos substituindo, obteremos:
Conseqüentemente
Outra maneira de colocar isso é:
O melhor caso possível é o meu original acima
5^m=7^n+s
, onde, ondes<7
.Então
T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)
como antes.O pior caso é quando só podemos encontrar k e st 5 ^ m = kx7 + s.
Outros casos estão em algum lugar entre eles. Seria interessante ver o quão bem podemos fazer por m muito grande, ou seja, quão bom podemos obter o termo de erro:
Parece impossível alcançar
e(m) = o(1)
em geral, mas espero que possamos provare(m)=o(m)
.A coisa toda então se baseia na distribuição dos dígitos de 7 árias de
5^m
para vários valores dem
.Tenho certeza de que há muita teoria por aí que cobre isso. Posso dar uma olhada e relatar em algum momento.
fonte
Aqui está uma implementação em Python funcional da resposta de Adam .
Eu gosto de lançar algoritmos que estou olhando para o Python, para que eu possa brincar com eles, pensei em publicá-lo aqui na esperança de que seja útil para alguém por aí, não que demorou muito tempo para se juntar.
fonte
rand5()
é um PRNG decente, então o loop não será infinita porque, eventualmente,5*(rand5() - 1) + rand5()
vai certamente ser <= 21)Por que não fazer isso simples?
As chances de obter 1 e 7 nesta solução são menores devido ao módulo, no entanto, se você quer apenas uma solução rápida e legível, este é o caminho a seguir.
fonte
Supondo que rand (n) aqui significa "número inteiro aleatório em uma distribuição uniforme de 0 a n-1 ", aqui está um exemplo de código usando o randint do Python, que tem esse efeito. Ele usa apenas randint (5) e constantes para produzir o efeito de randint (7) . Um pouco bobo, na verdade
fonte
do ... while
. Poderia ter sido1337
, ou12345
, ou qualquer número> 1.A premissa por trás da resposta correta de Adam Rosenfield é:
Quando n é igual a 2, você tem 4 possibilidades de descarte: y = {22, 23, 24, 25}. Se você usar n é igual a 6, você tem apenas 1 descarte: y = {15625}.
5 ^ 6 = 15625
7 * 2232 = 15624
Você chama rand5 mais vezes. No entanto, você tem uma chance muito menor de obter um valor de descarte (ou um loop infinito). Se existe uma maneira de não obter um valor possível de descarte para y, ainda não o encontrei.
fonte
Aqui está a minha resposta:
É um pouco mais complicado do que outros, mas acredito que minimiza as chamadas para o rand5. Como em outras soluções, há uma pequena probabilidade de que ele possa se repetir por um longo tempo.
fonte
Simples e eficiente:
(Inspirado em Qual é o seu desenho animado favorito de "programador"? ).
fonte
Enquanto não houver sete possibilidades para escolher, desenhe outro número aleatório, que multiplique o número de possibilidades por cinco. Em Perl:
fonte
$possibilities
sempre tem que crescer para 25 para sair do loop e retornar. Portanto, seu primeiro resultado é[0-124] % 7
, que não é distribuído uniformemente porque125 % 7 != 0
(na verdade, é 6).Não gosto de intervalos a partir de 1, então vou começar de 0 :-)
fonte
from collections import defaultdict def r7(n): if not n: yield [] else: for i in range(1, 6): for j in r7(n-1): yield [i] + j def test_r7(): d = defaultdict(int) for x in r7(6): s = (((((((((x[5] * 5) + x[4]) * 5) + x[3]) * 5) + x[2]) * 5) + x[1]) * 5) + x[0] if s <= 15623: d[s % 7] += 1 print d
Lá vai você, distribuição uniforme e zero rand5 chamadas.
Precisa colocar sementes com antecedência.
fonte
Sei que foi respondido, mas parece que está funcionando bem, mas não posso dizer se existe algum viés. Meu 'teste' sugere que é, pelo menos, razoável.
Talvez Adam Rosenfield tenha a gentileza de comentar?
Minha idéia (ingênua?) É esta:
Acumule rand5's até que haja bits aleatórios suficientes para criar um rand7. Isso leva no máximo 2 rand5's. Para obter o número rand7, uso o valor acumulado mod 7.
Para evitar o transbordamento do acumulador, e como o acumulador é o mod 7, eu uso o mod 7 do acumulador:
A função rand7 () segue:
(Eu deixei o intervalo de rand5 ser 0-4 e rand7 também é 0-6.)
Edit: Adicionado resultados para 100 milhões de tentativas.
Funções de rand 'reais' mod 5 ou 7
rand5: avg = 1.999802 0: 20003944 1: 19999889 2: 20003690 3: 19996938 4: 19995539 rand7: avg = 3.000111 0: 14282851 1: 14282879 2: 14284554 3: 14288546 4: 14292388 5: 14288736 6: 14280046
Meu rand7
A média parece boa e as distribuições de números também parecem boas.
randt: avg = 3.000080 0: 14288793 1: 14280135 2: 14287848 3: 14285277 4: 14286341 5: 14278663 6: 14292943
fonte
Existem algoritmos elegantes citados acima, mas aqui está uma maneira de abordá-lo, embora possa ser indireto. Estou assumindo valores gerados a partir de 0.
R2 = gerador de números aleatórios que fornece valores menores que 2 (espaço da amostra = {0, 1})
R8 = gerador de números aleatórios que fornece valores menores que 8 (espaço da amostra = {0, 1, 2, 3, 4, 5, 6, 7 })
Para gerar R8 a partir do R2, você executará o R2 três vezes e usará o resultado combinado de todas as 3 execuções como um número binário com 3 dígitos. Aqui está o intervalo de valores quando o R2 é executado três vezes:
0 0 0 -> 0
.
.
1 1 1 -> 7
Agora, para gerar R7 a partir de R8, simplesmente rodamos R7 novamente se retornar 7:
A solução indireta é gerar R2 a partir de R5 (assim como geramos R7 a partir de R8), depois R8 a partir de R2 e R7 a partir de R8.
fonte
Aqui está uma solução que se encaixa inteiramente em números inteiros e está dentro de cerca de 4% do ideal (ou seja, usa 1,26 números aleatórios em {0..4} para cada um em {0..6}). O código está em Scala, mas a matemática deve ser razoavelmente clara em qualquer idioma: você tira vantagem do fato de 7 ^ 9 + 7 ^ 8 estar muito próximo de 5 ^ 11. Então, você escolhe um número de 11 dígitos na base 5 e, em seguida, interpreta-o como um número de 9 dígitos na base 7, se estiver no intervalo (fornecendo 9 números base 7) ou como um número de 8 dígitos, se estiver acima do número de 9 dígitos, etc. .:
Se você colar um teste no intérprete (REPL, na verdade), obtém:
A distribuição é agradável e plana (dentro de cerca de 10k de 1/7 de 10 ^ 8 em cada compartimento, como esperado de uma distribuição aproximadamente gaussiana).
fonte
Usando um total contínuo , você pode
Esses dois problemas são um problema com as
rand(5)+rand(5)...
soluções simplistas do tipo. O código Python a seguir mostra como implementá-lo (a maioria disso está provando a distribuição).E esta saída mostra os resultados:
Um simplista
rand(5)+rand(5)
, ignorando os casos em que isso retorna mais de 6, tem uma variação típica de 18%, 100 vezes a do método mostrado acima:E, seguindo o conselho da Nixuz, limpei o script para que você possa extrair e usar as
rand7...
coisas:fonte
Essa resposta é mais um experimento para obter o máximo de entropia possível da função Rand5. Portanto, não é claro e quase certamente muito mais lento que outras implementações.
Assumindo a distribuição uniforme de 0-4 e a distribuição uniforme resultante de 0-6:
O número de bits adicionados ao buffer por chamada para Rand5 é atualmente de 4/5 * 2, portanto 1.6. Se o valor da probabilidade 1/5 estiver incluído, aumenta em 0,05 para 1,65, mas veja o comentário no código em que tive que desativar isso.
Bits consumidos pela chamada para Rand7 = 3 + 1/8 * (3 + 1/8 * (3 + 1/8 * (...
Isso é 3 + 3/8 + 3/64 + 3/512 ... então aproximadamente 3,42
Ao extrair informações dos setes, recupero 1/8 * 1/7 bits por chamada, aproximadamente 0,018
Isso fornece um consumo líquido de 3,4 bits por chamada, o que significa que a taxa é de 2,125 chamadas para Rand5 para cada Rand7. O ideal deve ser 2.1.
Eu imagino que essa abordagem seja significativamente mais lenta do que muitas outras aqui, a menos que o custo da ligação para o Rand5 seja extremamente caro (digamos, chamar alguma fonte externa de entropia).
fonte
em php
loops para produzir um número aleatório entre 16 e 127, divide por dezesseis para criar um ponto flutuante entre 1 e 7,9375 e, em seguida, arredonda para baixo para obter um int entre 1 e 7. Se não me engano, há uma chance de 16/112 de obter qualquer um dos 7 resultados.
fonte
fonte
7 = 111b
comp(7) = 8 / 125
Acho que tenho quatro respostas, duas dando soluções exatas como a de @Adam Rosenfield, mas sem o problema do loop infinito, e outras duas com solução quase perfeita, mas com implementação mais rápida que a primeira.
A melhor solução exata requer 7 chamadas
rand5
, mas vamos prosseguir para entender.Método 1 - Exato
A força da resposta de Adam é que ela fornece uma distribuição uniforme perfeita e há uma probabilidade muito alta (21/25) de que apenas duas chamadas para rand5 () serão necessárias. No entanto, o pior caso é o loop infinito.
A primeira solução abaixo também fornece uma distribuição uniforme perfeita, mas requer um total de 42 chamadas para
rand5
. Sem loops infinitos.Aqui está uma implementação do R:
Para pessoas que não estão familiarizadas com o R, aqui está uma versão simplificada:
A distribuição de
rand5
será preservada. Se fizermos as contas, cada uma das 7 iterações do loop possui 5 ^ 6 combinações possíveis, portanto, o número total de combinações possíveis é(7 * 5^6) %% 7 = 0
. Assim, podemos dividir os números aleatórios gerados em grupos iguais de 7. Veja o método dois para mais discussão sobre isso.Aqui estão todas as combinações possíveis:
Eu acho que é fácil mostrar que o método de Adam será muito mais rápido. A probabilidade de haver 42 chamadas ou mais
rand5
na solução de Adam é muito pequena ((4/25)^21 ~ 10^(-17)
).Método 2 - Não exato
Agora, o segundo método, que é quase uniforme, mas requer 6 chamadas para
rand5
:Aqui está uma versão simplificada:
Esta é essencialmente uma iteração do método 1. Se gerarmos todas as combinações possíveis, aqui estão as contagens resultantes:
Um número aparecerá mais uma vez nos
5^6 = 15625
testes.Agora, no método 1, adicionando 1 a 6, movemos o número 2233 para cada um dos pontos sucessivos. Assim, o número total de combinações corresponderá. Isso funciona porque 5 ^ 6 %% 7 = 1 e, depois, fazemos 7 variações apropriadas (7 * 5 ^ 6 %% 7 = 0).
Método 3 - Exato
Se o argumento dos métodos 1 e 2 for entendido, o método 3 segue e requer apenas 7 chamadas para
rand5
. Nesse ponto, acho que esse é o número mínimo de chamadas necessárias para uma solução exata.Aqui está uma implementação do R:
Para pessoas que não estão familiarizadas com o R, aqui está uma versão simplificada:
A distribuição de
rand5
será preservada. Se fizermos as contas, cada uma das 7 iterações do loop tem 5 resultados possíveis, portanto, o número total de combinações possíveis(7 * 5) %% 7 = 0
. Assim, podemos dividir os números aleatórios gerados em grupos iguais de 7. Veja o método um e dois para mais discussão sobre isso.Aqui estão todas as combinações possíveis:
Eu acho que é fácil mostrar que o método de Adam ainda funcionará mais rápido. A probabilidade de que haja 7 ou mais chamadas
rand5
na solução de Adam ainda é pequena ((4/25)^3 ~ 0.004
).Método 4 - Não exato
Essa é uma variação menor do segundo método. É quase uniforme, mas requer 7 chamadas para
rand5
, isto é mais um para o método 2:Aqui está uma versão simplificada:
Se gerarmos todas as combinações possíveis, aqui estão as contagens resultantes:
Dois números aparecerão uma vez menos nos
5^7 = 78125
testes. Para a maioria dos propósitos, eu posso viver com isso.fonte
i=7
também não tem nenhum efeito, uma vez que a adição7*rand5()
der
não alterar o valor dor
mod 7.)A função que você precisa é rand1_7 () , escrevi rand1_5 () para que você possa testá-lo e plotá-lo.
fonte