Rand5 () a Rand7 () [fechado]


29

Você recebe uma função Rand5 (). Essa função retorna números inteiros perfeitamente aleatórios (distribuição igual) entre 1 e 5.

Forneça a função Rand7 (), que usa Rand5 () para produzir números inteiros perfeitamente aleatórios entre 1 e 7.



8
Obrigatório xkcd: xkcd.com/221
Steven Rumbalski

1 e 5, inclusive? ou seja, do conjunto {1,2,3,4,5}?
Aaron McDaid

11
Quais critérios determinam um único vencedor?
Kojiro

Aquele momento em que você percebe que essa é realmente uma pergunta antiga.
nyuszika7h

Respostas:


11

Java - 61 caracteres

int rand7(){int s=0,c=7;while(c-->0)s+=rand5();return s%7+1;}

Driver de teste para validação:

class Rand {

    public static void main(String[] args) {
        int[] nums = new int[7];
        // get a lot of numbers
        for(int i = 0; i < 10000000; i++) nums[rand7()-1]++;
        // print the results
        for(int i = 0; i < 7; i++) System.out.println((i+1) + ": " + nums[i]);
    }

    // just for rand5()
    static java.util.Random r = new java.util.Random();

    static int rand5() {
        return r.nextInt(5)+1; // Random.nextInt(n) returns 0..n-1, so add 1
    }

    static int rand7(){int s=0,c=7;while(c-->0)s+=rand5();return s%7+1;}

}

Resultados

C:\Documents and Settings\glowcoder\My Documents>java Rand
1: 1429828
2: 1429347
3: 1428328
4: 1426486
5: 1426784
6: 1429853
7: 1429374

C:\Documents and Settings\glowcoder\My Documents>

10
Pontos extras para o "vai para o operador"
Steve P

fazer a barba? int rand7 () {for (int s = 0, c = 7; c -> 0; s + = rand5 ()); retorne s% 7 + 1;}
Ron

3
Esta resposta não está correta: as probabilidades desta função retornando os valores de 1 a 7 são 0,1430656, 0,1430016, 0,1428224, 0,1426432, 0,1426432, 0,1428224 e 0,1430016, respectivamente. Sim, a diferença entre as probabilidades mínima e máxima é menor que 0,0005, mas ainda assim a pergunta especificava "números inteiros perfeitamente aleatórios".
Ilmari Karonen

@ilmari Você está certo - Eu corri um teste e parece que a distribuição não é mesmo ... deixe-me pensar sobre isso ...
corsiKa

11
@userunknown: Sim, as probabilidades que publiquei não são realmente aproximações, elas são exatas (0,1430656 = 11177/78125, etc.), assumindo uma aleatoriamente perfeita rand5. Eu os calculei no Maple usando álgebra matricial simples, mas você pode fazê-lo com lápis e papel em alguns minutos, se quiser. De qualquer forma, o Omar já postou os mesmos números (sem fator de normalização) em um comentário para outra resposta alguns dias antes. (Também ps., Você só pode @ notificar um usuário por comentário, embora o autor da publicação seja sempre notificado em qualquer caso.)
Ilmari Karonen

7

Perl - 47 (tinha 52) caracteres

sub rand7{($x=5*&rand5+&rand5-3)<24?int($x/3):&rand7} 

Além disso, uso o operador ternário AND recursão. Melhor dia de todos!

OK, 47 caracteres se você usar mod em vez de div:

sub rand7{($x=5*&rand5+&rand5)<27?$x%7+1:&rand7} 

Tão perto ... substitua o 30 por 27 (= 6 + 21) e você obterá uma distribuição perfeitamente uniforme. Ah, e você pode soltar os dois últimos &sinais para reduzi-lo para 46 caracteres (incluindo o espaço, que coloca sua versão atual em 48).
Ilmari Karonen

7

JavaScript, 42

Rand7=f=_=>(x=Rand5()+Rand5()*5-5)>7?f():x

Coisa bônus ES5:

Rand7=eval.bind(0,'for(;x=Rand5()+Rand5()*5-5,x>7;);x')

6

Ruby - 54 caracteres (baseado na solução Dan McGrath, usando loop)

def rand7;x=8;while x>7 do x=rand5+5*rand5-5 end;x;end

Ruby - 45 caracteres (mesma solução, usando recursão)

def rand7;x=rand5+5*rand5-5;x>7 ?rand7: x;end

Pode ser reduzido por 1 caractere usando (x=rand5+5*rand5-5)>7?.
Lars Haugseth

5

Em Python:

def Rand7():
  while True:
    x = (Rand5() - 1) * 5 + (Rand5() - 1)
    if x < 21: return x/3 + 1

4

Em caracteres comuns do Lisp 70:

(defun rand7()(let((n(-(+(rand5)(* 5(rand5)))5)))(if(> n 7)(rand7)n)))

Os parênteses ocupam mais espaço do que eu gostaria.


Agradável. Você pode extrair mais dois caracteres criando uma variável global:(defun rand7()(setq n(-(+(rand5)(* 5(rand5)))5))(if(> n 7)(rand7)n))
Dr. Pain

Ainda melhor:(defun rand7()(if(>(setq n(-(+(rand5)(* 5(rand5)))5))7)(rand7)n))
Dr. Pain

4

Em c / c ++ usando amostragem por rejeição

int rand7(){int x=8;while(x>7)x=rand5()+5*rand5()-5;return x;}

62 caracteres.


@ barrycarter: A condição é while(x>7), de modo que só seria satisfeita por números no intervalo válido.
precisa saber é o seguinte

Minha culpa. Excluiu meu comentário idiota.
precisa saber é o seguinte

@ barry E então você deixou outro. ;)
Mateen Ulhaq

Levei alguns minutos para perceber como a matemática aqui produz uma distribuição aleatória uniformemente que pode ser usada para amostragem por rejeição.
Daniel

3

Tradução para PHP, a partir da resposta publicada por Dan McGrath.

function Rand7(){$x=8;while($x>7)$x=rand5()+5*rand5()-5;return $x;}

67 caracteres.


Isso não deve ser prefixado pela palavra "função" (e um espaço)?
Jtjacques

Sim ... e agora são 67 caracteres ...
Marc-François

3

R, 34 caracteres

Em R (uma linguagem criada para computação estatística), uma solução deliberadamente trapaceira:

# Construct a Rand5 function
Rand5 <- function() sample(seq(5),1)
# And the golf
Rand7=function(r=Rand5())sample(1:(r/r+6),1)
# Or (same character count)
Rand7=function(r=Rand5())sample.int(r/r+6,1)
# Or even shorter(thanks to @Spacedman)
Rand7=function()sample(7)[Rand5()]

Graças à preguiçosa avaliação dos argumentos, eliminei o ponto-e-vírgula e o aparelho.

A saída acima de 10 ^ 6 replica:

> test <- replicate(10^6,Rand7())
> table(test)
test
     1      2      3      4      5      6      7 
142987 142547 143133 142719 142897 142869 142848 

library(ggplot2)
qplot(test)

histograma de resultados


2
Se você estiver indo para ser cheaterish, você pode muito bem ser o melhor trapaceiro pode ser:Rand7=function(){r=Rand5();sample(7)[r]}
Spacedman

Se você vai fazer isso, por que se preocupar com o armazenamento intermediário? Rand7=function(){sample(7)[Rand5()]}
quer

@BrianDiggs Caminho-dependência em ação .... :-)
Ari B. Friedman

3

scala, 47, 40 59 caracteres:

def rand7:Int={val r=5*(rand5-1)+rand5
if(r<8)r else rand7}

com 2 entradas da rand5:

\ 1 2 3 4 5 
1 1 2 3 4 5  
2 6 7 8 ..
3 11 ..
4 ..
5

Multiplico o primeiro-1 por 5 e adiciono o segundo. A maioria dos resultados é ignorada e leva a um novo cálculo. O resultado deve ser uma distribuição igual de valores de 1 a 25, da qual seleciono apenas os 7 primeiros. Eu poderia aceitar os 21 primeiros com a construção de um módulo, mas isso levaria a um código mais longo.

código histórico que falhou, mas não muito obviamente. Agradecemos a Ilmari Karonen por apontar:

def rand7=(1 to 7).map(_=>rand5).sum%7+1

Graças a Yoshiteru Takeshita, por essa abordagem de scala-2.8.0 que tornou a 'soma' tão fácil. Minha solução antes:

def rand7=((0/:(1 to 7))((a,_)=>a+rand5-1))%7+1

rand5:

val rnd = util.Random 
def rand5 = rnd.nextInt (5) + 1


Usuário Yoshiteru Takeshita propôs uma redução de 40 caracteres para Scala 2.8.0 ou posteriordef rand7=(1 to 7).map(_=>rand5).sum%7+1
Peter Taylor

Esta solução também não está correta, consulte os comentários da resposta do glowcoder .
Ilmari Karonen

@IlmariKaronen: Você está certo - eu reformulei minha solução.
usuário desconhecido

3

C ++

int Rand4()
{
    int r = Rand5();
    return r > 4 ? Rand4() : r;
}

inline int Rand8()
{    
    return (Rand4() - 1) << 2 + Rand4();
}

int Rand7()
{
    int r = Rand8();
    return r > 7 ? Rand7() : r;
}

C ++ (109)

Golfe

int Rand4(){int r=Rand5();return r>4?Rand4():r;}int Rand7(){int r=Rand4()-1<<2+Rand4();return r>7?Rand7():r;}

Eu realmente não acho que você possa chamar isso de "um liner" porque ponto e vírgula define uma linha de código em C ++.
Peter Olson

@ Peter Oh, bem, ele nem exige mais one-liners.
Mateen Ulhaq

Ele retornou um número de 1 a 8.
jimmy23013 20/12/2014

2

Tradução para Javascript, a partir da resposta postada por Dan McGrath.

function Rand7(){x=8;while(x>7)x=rand5()+5*rand5()-5;return x}

62 caracteres


11
function Rand7(){for(x=8;x>7;x=rand5()+5*rand5()-5);return x}é um pouco mais curto: P
JiminP 23/06

2

JavaScript, 85

function Rand7(){for(x=0,i=1;i<8;x^=i*((k=Rand5())%2),i*=1+(k<5));return x?x:Rand7()}

Eu sei que há uma resposta mais curta, mas eu queria mostrar o teste desse quebra-cabeça. Acontece que apenas a resposta de Clyde Lobo usando a amostragem de rejeição de Dan McGrath está correta (entre as respostas do JS).


2

С ++

int Rand7()
{
    int r = Rand5();
    int n = 5;
    do {
        r = (r - 1) * 5 + Rand5();
        int m = n * 5 / 7 * 7;
        if (r <= m) {
            return r % 7 + 1;
        }
        r -= m;
        n = n * 5 - m;
    } while (1);
}

Distribuição de números (1000000 números inteiros):

142935 142751 142652 143299 142969 142691 142703

O número médio de chamadas para Rand5 () por cada número inteiro gerado é de cerca de 2,2 (2 a 10 ou mais).

1 2      3      4     5    6   7 8  9 10
0 840180 112222 44433 2212 886 0 60 6 1

2

Em Java (ou C / C ++, suponho)

  • usando a fórmula de geração de Alexandru, em 65 caracteres:

    int rand7(){int x=rand5()*5+rand5()-6;return x>20?rand7():x/3+1;}
    
  • usando a fórmula de geração de Dan McGrath, em 60 caracteres

    int rand7(){int x=rand5()+5*rand5()-5;return x>7?rand7():x;}
    

1

Clojure - 58 chars

(defn rand7[](#(if(<% 8)%(rand7))(+(rand5)(*(rand5)5)-5)))

1

Python, 56 37 chars

Another solution that may be wrong, in Python:

rand7 = lambda: sum(rand5() for i in range(7)) % 7 + 1

This seems to be too simple, but when I try:

counter = [0] * 7
for i in range(100000):
     counter[rand7()] += 1

I get a reasonably even distribution (all between 14000 and 14500).

Okay, now as somebody voted for this post: Is this solution indeed correct? I more posted this here to make people criticize it. Well, if it is correct, my golfed version would be:

rand7=lambda:eval("+rand5()"*7)%7+1

which comes out to 37 chars.


Your solution is not correct: you base your decision on 7 rolls of a fair 5-sided die, which means there are 5^7 (5 to the 7th power) equiprobable outcomes. Since this is not a multiple of 7, you cannot return 7 equiprobable results. I don't think there's an easy formula for what you return; you can brute-force the computation or work it out by hand on smaller numbers (flip 3 coins (H=1, T=2) and sum the results).
Gilles 'SO- stop being evil'

1
Wow, the distribution you generate, though not uniform, is remarkably close: the exact proportion of the probabilities of each number is {1: 11177, 2: 11172, 3: 11158, 4: 11144, 5: 11144, 6: 11158, 7: 11172}
Omar


1

Python, 70 chars

def rand7():
 while True:
  n=5*(rand5()-1)+(rand5()-1)
  if n<21:return n%7+1

but completely correct based on the reasoning here.


1

Perl, 43 chars, iterative rejection sampling

sub rand7{1while($_=5*&rand5-rand5)>6;$_+1}

This gives a warning about Ambiguous use of -rand5 resolved as -&rand5(), but works correctly. Prepending an & also to the second rand5 call fixes it at the cost of one stroke. (Conversely, the other & can also be removed if rand5 has been defined with a () prototype.)

Ps. The following 46-char version is about three times faster:

sub rand7{1while($_=5*&rand5-rand5)>20;$_%7+1}

1

Java - 66 chars

int rand7(){int s;while((s=rand5()*5+rand5())<10);return(s%7+1);}

Longer than previous routine, but I think this one returns uniformly distributed numbers in less time.


1

PostScript (46)

This uses binary token encoding, therefore, here is a hexdump:

00000000  2f 72 61 6e 64 37 7b 38  7b 92 38 37 92 61 7b 92  |/rand7{8{.87.a{.|
00000010  40 7d 69 66 92 75 32 7b  72 61 6e 64 35 7d 92 83  |@}if.u2{rand5}..|
00000020  35 92 6c 92 01 35 92 a9  7d 92 65 7d 92 33        |5.l..5..}.e}.3|
0000002e

To try it out, you can also download it.

Here is the ungolfed and commented code, together with testing code.

% This is the actual rand7 procedure.
/rand7{
  8{                      % potentialResult
    % only if the random number is less than or equal to 7, we're done
    dup 7 le{             % result
      exit                % result
    }if                   % potentialResult
    pop                   % -/-
    2{rand5}repeat        % randomNumber1 randomNumber2
    5 mul add 5 sub       % randomNumber1 + 5*randomNumber2 - 5 = potentialResult
  }loop
}def

%Now, some testing code.

% For testing, we use the built-in rand operator; 
% Doesn't really give a 100% even distribution as it returns numbers
% from 0 to 2^31-1, which is of course not divisible by 5.
/rand5 {
  rand 5 mod 1 add
}def

% For testing, we initialize a dict that counts the number of times any number
% has been returned. Of course, we start the count at 0 for every number.
<<1 1 7{0}for>>begin

% Now we're calling the function quite a number of times 
% and increment the counters accordingly.
1000000 {
  rand7 dup load 1 add def
}repeat

% Print the results
currentdict{
  2 array astore ==
}forall

-1
int result = 0;

for (int i = 0; i++; i<7)
    if (((rand(5) + rand(5)) % 2) //check if odd
        result += 1;

return result + 1;

2
This won't give a uniform distribution. Look at the distribution of rand(5)+rand(5) over 10000 iterations to see why
gnibbler

result can be any number from 1 to 8 in your code...
Omar

Plus, as gnibbler said, the distribution is not uniform: (rand(5)+rand(5))%2 is biased towards 0, it produces 0 13 times for every 12 times it produces 1; i.e., the probabilities are proportional to {0: 13, 1: 12}. With that notation, the probalities for your function are proportional to {1: 62748517, 2 : 405451956, 3: 1122790032, 4: 1727369280, 5: 1594494720, 6: 883104768, 7: 271724544, 8: 35831808} (quite heavily skewed towards larger numbers). Or, fixing the loop to run 6 times, {1: 4826809, 2: 26733096, 3: 61691760, 4: 75928320, 5: 52565760, 6: 19408896, 7: 2985984}
Omar

-1

R (30 characters)

Define rand7:

rand7=function(n)sample(7,n,T)

Because R was written with statistical analysis in mind, this task is trivial, and I use the built-in function sample with replacement set to TRUE.

Sample output:

> rand7(20)
 [1] 4 3 6 1 2 4 3 2 3 2 5 1 4 6 4 2 4 6 6 1
> rand7(20)
 [1] 1 2 5 2 6 4 6 1 7 1 1 3 7 6 4 7 4 2 1 2
> rand7(20)
 [1] 6 7 1 3 3 1 5 4 3 4 2 1 5 4 4 4 7 7 1 5

1
It does say you have to use Rand5. Doesn't say how, but you have to use it...
Spacedman

@Spacedman Yes, I explicitly ignored it. That is use by non-reference.
Andrie

-1

Groovy

rand7={if(b==null)b=rand5();(b=(rand5()+b)%7+1)}

example distribution over 35,000 iterations:

[1:5030, 2:4909, 3:5017, 4:4942, 5:5118, 6:4956, 7:5028]

Is it bad that it's stateful?



-1

How about this?

int Rand7()
{
    return Rand5()+ Rand5()/2;
}

Whatever language that is, does its / operator do integer math? What happens to your results if it does decimal, floating-point, or integer math?
kojiro

Assuming integer division, this function has the following distribution: [2/25, 4/25, 5/25, 5/25, 5/25, 3/25, 1/25]. Not exactly uniform.
primo

primo is right. adding random numbers is generally going to skew the probabilities toward the middle values.
gnibbler

-1

Java - 54

int m=0;int rand7(){return(m=m*5&-1>>>1|rand5())%7+1;}

Distribution test: [1000915, 999689, 999169, 998227, 1001653, 1000419, 999928]

Algorithm:

  • Keep a global variable
  • multiply by 5, so there get 5 places free at the least significant end
  • Truncate the sign bit to make it positive (not necessary if unsigned numbers were supported)
  • Modulo 7 is the answer

> The numbers are not mutually uncorrelated anymore, but individually perfectly random.


-1

Ruby (43 bytes)

def rand7;(0..7).reduce{|i|i+rand5}%7+1;end

cemper93's solution ported to Ruby is three bytes shorter ;) (34 bytes)

def rand7;eval("+rand5"*7)%7+1;end

-3

C/C++ code the core code has one line only!

static unsigned int gi = 0;

int rand7()
{
    return (((rand() % 5 + 1) + (gi++ % 7)) % 7) + 1;
}

//call this seed before rand7
//maybe it's not best seed, if yo have any good idea tell me please
//and thanks JiminP again, he remind me to do this
void srand7()
{
    int i, n = time(0);
    for (i = 0; i < n % 7; i++)
        rand7();
}

The srand7() is the seed of rand7, must call this function before rand7, just like call srand before rand in C.

This is a very good one, because it call rand() only one time, and no loop thing, no expends extra memories.

Let me explain it: consider a integer array with size of 5:

1st get one number from 1 2 3 4 5 by rand5
2nd get one number from 2 3 4 5 6
3rd get one number from 3 4 5 6 7
4th get one number from 4 5 6 7 1
5th get one number from 5 6 7 1 2
5th get one number from 6 7 1 2 3
7th get one number from 7 1 2 3 4

So we got the TABLE, each one of 1-7 appears 5 times in it, and has all 35 numbers, so the probability of each number is 5/35=1/7. And next time,

8th get one number from 1 2 3 4 5
9th get one number from 2 3 4 5 6
......

After enough times, we can get the uniform distribution of 1-7.

So, we can allocate a array to restore the five elements of 1-7 by loop-left-shift, and get one number from array each time by rand5. Instead, we can generate the all seven arrays before, and using them circularly. The code is simple also, has many short codes can do this.

But, we can using the properties of % operation, so the table 1-7 rows is equivalent with (rand5 + i) % 7, that is : a = rand() % 5 + 1 is rand5 in C language, b = gi++ % 7 generates all permutations in table above, and 0 - 6 replace 1 - 7 c = (a + b) % 7 + 1, generates 1 - 7 uniformly. Finally, we got this code:

(((rand() % 5 + 1) + (gi++ % 7)) % 7) + 1 

But, we can not get 6 and 7 at first call, so we need a seed, some like srand for rand in C/C++, to disarrange the permutation for first formal call.

Here is the full code to testing:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static unsigned int gi = 0;

//a = rand() % 5 + 1 is rand5 in C language,
//b = gi++ % 7 generates all permutations,
//c = (a + b) % 7 + 1, generates 1 - 7 uniformly.
//Dont forget call srand7 before rand7
int rand7()
{
   return (((rand() % 5 + 1) + (gi++ % 7)) % 7) + 1;
}

//call this seed before rand7
//maybe it's not best seed, if yo have any good idea tell me please
//and thanks JiminP again, he remind me to do this
void srand7()
{
    int i, n = time(0);
    for (i = 0; i < n % 7; i++)
        rand7();
}

void main(void)
{
    unsigned int result[10] = {0};
    int k;

    srand((unsigned int)time(0)); //initialize the seed for rand
    srand7() //initialize the rand7

    for (k = 0; k < 100000; k++)
        result[rand7() - 1]++;

    for (k = 0; k < 7; k++)
        printf("%d : %.05f\n", k + 1, (float)result[k]/100000);
}

It 'passes' the 'test', but that doesn't mean this is a good random function. Can I get 6 or 7 by calling it once?
JiminP

But there's good kinds and bad kinds of approximation. And this code is bad - because it does not gives uniform distribution when called only once. If one wrote something like int main(){if(rand7()==6) printf("Hello, world!");}, approximation using loop will print 'Hello, world!' 1 in 7 times, but your code doesn't.
JiminP

thank you @JiminP! you are right for 6,7 at first time. i need a seed to disarrange before call rand7, the seed just like the srand in C/C++. i fixed my code, and thank you again!!!
Sean

hm....the srand10 does not work, the last 3 numbers can not get at 10, 20, 30 ...positions. sorry @JiminP, but how to modify it? i think this is hopeful way.
Sean

2
Different calls to this function are not independent of each other. The spec here doesn't require this, but that is typically expecetd of random number generators. Otherwise, you could say, return a random uniform number the first time and in future calls just return (previous+1)%7...
Omar
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.