Penso di avere quattro risposte, due che danno soluzioni esatte come quella di @Adam Rosenfield ma senza il problema del loop infinito, e altre due con una soluzione quasi perfetta ma un'implementazione più veloce della prima.
La migliore soluzione esatta richiede 7 chiamate a rand5
, ma consente di procedere per capire.
Metodo 1 - Esatto
Il punto di forza della risposta di Adam è che fornisce una distribuzione uniforme perfetta, e c'è un'altissima probabilità (21/25) che saranno necessarie solo due chiamate a rand5 (). Tuttavia, il caso peggiore è loop infinito.
La prima soluzione di seguito fornisce anche una distribuzione uniforme perfetta, ma richiede un totale di 42 chiamate rand5
. Nessun loop infinito.
Ecco un'implementazione R:
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(0:6, function(i) i + rand5() + rand5()*2 + rand5()*3 + rand5()*4 + rand5()*5 + rand5()*6)) %% 7) + 1
Per le persone che non hanno familiarità con R, ecco una versione semplificata:
rand7 = function(){
r = 0
for(i in 0:6){
r = r + i + rand5() + rand5()*2 + rand5()*3 + rand5()*4 + rand5()*5 + rand5()*6
}
return r %% 7 + 1
}
La distribuzione di rand5
sarà preservata. Se facciamo la matematica, ognuna delle 7 iterazioni del loop ha 5 ^ 6 possibili combinazioni, quindi il numero totale di possibili combinazioni sono (7 * 5^6) %% 7 = 0
. Quindi possiamo dividere i numeri casuali generati in gruppi uguali di 7. Vedi il metodo due per ulteriori discussioni su questo.
Ecco tutte le possibili combinazioni:
table(apply(expand.grid(c(outer(1:5,0:6,"+")),(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
15625 15625 15625 15625 15625 15625 15625
Penso che sia semplice dimostrare che il metodo di Adam verrà eseguito molto più velocemente. La probabilità che ci siano 42 o più chiamate a rand5
nella soluzione di Adam è molto piccola ( (4/25)^21 ~ 10^(-17)
).
Metodo 2 - Non Esatto
Ora il secondo metodo, che è quasi uniforme, ma richiede 6 chiamate a rand5
:
rand7 <- function() (sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
Ecco una versione semplificata:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return r %% 7 + 1
}
Questa è essenzialmente un'iterazione del metodo 1. Se generiamo tutte le possibili combinazioni, ecco i conteggi risultanti:
table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
2233 2232 2232 2232 2232 2232 2232
Un numero apparirà ancora una volta nelle 5^6 = 15625
prove.
Ora, nel Metodo 1, aggiungendo da 1 a 6, spostiamo il numero 2233 in ciascuno dei punti successivi. Pertanto, il numero totale di combinazioni corrisponderà. Questo funziona perché 5 ^ 6 %% 7 = 1, e quindi facciamo 7 variazioni appropriate, quindi (7 * 5 ^ 6 %% 7 = 0).
Metodo 3 - Esatto
Se l'argomento del metodo 1 e 2 è compreso, il metodo 3 segue e richiede solo 7 chiamate rand5
. A questo punto, ritengo che questo sia il numero minimo di chiamate necessarie per una soluzione esatta.
Ecco un'implementazione R:
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(1:7, function(i) i * rand5())) %% 7) + 1
Per le persone che non hanno familiarità con R, ecco una versione semplificata:
rand7 = function(){
r = 0
for(i in 1:7){
r = r + i * rand5()
}
return r %% 7 + 1
}
La distribuzione di rand5
sarà preservata. Se facciamo la matematica, ognuna delle 7 iterazioni del ciclo ha 5 possibili esiti, quindi il numero totale di possibili combinazioni lo sono (7 * 5) %% 7 = 0
. Quindi possiamo dividere i numeri casuali generati in gruppi uguali di 7. Vedi il metodo uno e due per ulteriori discussioni su questo.
Ecco tutte le possibili combinazioni:
table(apply(expand.grid(0:6,(1:5)),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
5 5 5 5 5 5 5
Penso che sia semplice dimostrare che il metodo di Adam sarà ancora più veloce. La probabilità che ci siano 7 o più chiamate a rand5
nella soluzione di Adam è ancora piccola ( (4/25)^3 ~ 0.004
).
Metodo 4 - Non esatto
Questa è una variante minore del secondo metodo. È quasi uniforme, ma richiede 7 chiamate a rand5
, che è un'ulteriore al metodo 2:
rand7 <- function() (rand5() + sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
Ecco una versione semplificata:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return (r+rand5()) %% 7 + 1
}
Se generiamo tutte le possibili combinazioni, ecco i conteggi risultanti:
table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6,1:5),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
11160 11161 11161 11161 11161 11161 11160
Due numeri appariranno una volta in meno nelle 5^7 = 78125
prove. Per molti scopi, posso conviverci.