Rand5()至Rand7()


29

为您提供了一个函数Rand5()。此函数返回1到5之间的完全随机(均分布)整数。

提供函数Rand7(),该函数使用Rand5()产生1到7之间的完全随机整数。




1和5包括在内?即从集合{1,2,3,4,5}中?
亚伦·麦克戴德

1
什么条件确定一个获胜者?
kojiro 2013年

当您意识到这实际上是一个古老的问题时。
nyuszika7h 2014年

Answers:


11

Java-61个字符

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

测试驱动程序以进行验证:

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;}

}

结果

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
“为操作员做准备”的加分
Steve P

刮个炭?int rand7(){for(int s = 0,c = 7; c-> 0; s + = rand5()); return s%7 + 1;}
罗恩

3
这个答案是不正确的:此函数返回值从1到7的概率分别为0.1430656、0.1430016、0.1428224、0.1426432、0.1426432、0.1428224和0.1430016。是的,最小和最大概率之差小于0.0005,但是问题仍然指定为“完全随机整数”。
Ilmari Karonen 2011年

@ilmari你是对的-我只是进行了一次测试,似乎分布甚至没有...让我考虑一下...
corsiKa 2011年

1
@userunknown:是的,我发布的概率实际上不是近似值,假设完全随机,它们是精确的(0.1430656 = 11177/78125等)rand5。我使用简单的矩阵代数在Maple中对它们进行了计算,但是如果需要,您可以在几分钟内用铅笔和纸来进行计算。无论如何,事实证明,奥马尔已经在几天前对另一个答案的评论中发布了相同的数字(无标准化系数)。(此外,尽管在任何情况下都会始终通知该帖子的作者,但每个评论您只能@notify通知一位用户。)
Ilmari Karonen 2011年

7

Perl-47(原52)字符

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

另外,我可以使用三元运算符和递归。最好的一天!

OK,如果使用mod而不是div,则为47个字符:

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

如此接近...将30替换为27(= 6 + 21),您将获得完全均匀的分布。哦,您可以删除最后两个&符号以将其减少到46个字符(包括空格,这会使您的当前版本为48个字符)。
Ilmari Karonen 2011年

7

JavaScript,42岁

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

奖励ES5:

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

6

Ruby-54个字符(基于Dan McGrath解决方案,使用循环)

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

Ruby-45个字符(相同的解决方案,使用递归)

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

可以使用缩短1个字符(x=rand5+5*rand5-5)>7?
拉尔斯·豪格斯

5

在Python中:

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

4

使用Lisp常见字符70个:

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

括号占用的空间超出了我的期望。


真好 您可以通过设置na全局变量来挤出另外两个字符:(defun rand7()(setq n(-(+(rand5)(* 5(rand5)))5))(if(> n 7)(rand7)n))
Dr. Pain

甚至更好:(defun rand7()(if(>(setq n(-(+(rand5)(* 5(rand5)))5))7)(rand7)n))
Pain博士

4

在C / C ++中使用拒绝采样

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

62个字符。


@barrycarter:条件是while(x>7),因此只能由有效范围内的数字来满足。
mellamokb 2011年

我的错。删除了我的愚蠢评论。
barrycarter 2011年

@barry然后您又离开了一个。;)
Mateen Ulhaq 2011年

我花了几分钟时间才意识到这里的数学如何产生均匀的随机分布,可用于拒绝采样。
丹尼尔(Daniel)


3

R,34个字符

在R(一种用于统计计算的语言)中,故意作弊的解决方案是:

# 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()]

由于对参数的懒惰评估,我消除了分号和花括号。

输出超过10 ^ 6个副本:

> 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)

histogram of results


2
如果您要作弊,那么您也可以成为最好的作弊者:Rand7=function(){r=Rand5();sample(7)[r]}
Spacedman 2011年

如果要这样做,为什么还要打扰中间存储呢?Rand7=function(){sample(7)[Rand5()]}
Brian Diggs

@BrianDiggs行动中的路径依赖... :-)
Ari B. Friedman

3

阶,47,40 59个字符:

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

来自rand5的2个输入:

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

我将第一个1乘以5,然后加上第二个。大多数结果将被忽略,并导致进行新的计算。结果应该是从1到25的值相等分布,从中我只选择前7个值。我可以接受构建模的前21个,但这将导致更长的代码。

历史代码失败了,但不是很明显。感谢Ilmari Karonen指出:

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

感谢竹下佳辉(Yoshiteru Takeshita)提出的scala-2.8.0方法使“ sum”变得如此简单。我之前的解决方案:

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

rand5:

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


用户Yoshiteru Takeshita建议将Scala 2.8.0或更高版本的字符减少为40个字符,因为def rand7=(1 to 7).map(_=>rand5).sum%7+1
Peter Taylor

此解决方案也不正确,请参见glowcoder的答案的注释。
Ilmari Karonen 2011年

@IlmariKaronen:您是对的-我重新设计了解决方案。
用户未知,

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)

打高尔夫球

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

我真的不认为您可以将其称为“一个衬里”,因为分号在C ++中定义了一行代码。
彼得·奥尔森

@Peter Oh well, it doesn't even require one-liners anymore.
Mateen Ulhaq

It returned a number from 1 to 8.
jimmy23013

2

Translation to Javascript, from the answer posted by Dan McGrath.

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

62 chars


1
function Rand7(){for(x=8;x>7;x=rand5()+5*rand5()-5);return x} is little shorter :P
JiminP

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()}

I know there's shorter answer, but I wanted to show the test of this puzzle. It turns out that only Clyde Lobo's answer using Dan McGrath's rejection sampling is correct (between JS answers).


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);
}

Numbers distribution (1000000 integers):

142935 142751 142652 143299 142969 142691 142703

Average number of calls to Rand5() per every generated integer is about 2.2 (2 to 10+).

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

2

In Java (or C/C++ I suppose)

  • using generation formula by Alexandru, in 65 characters:

    int rand7(){int x=rand5()*5+rand5()-6;return x>20?rand7():x/3+1;}
    
  • using generation formula by Dan McGrath, in 60 characters

    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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.