给定一个产生1到5范围内随机整数的函数,写一个产生1到7范围内随机整数的函数。
答案 0 :(得分:558)
这相当于亚当罗森菲尔德的解决方案,但对于一些读者可能会更清楚一些。它假设rand5()是一个返回1到5范围内的统计随机整数的函数。
int rand7()
{
int vals[5][5] = {
{ 1, 2, 3, 4, 5 },
{ 6, 7, 1, 2, 3 },
{ 4, 5, 6, 7, 1 },
{ 2, 3, 4, 5, 6 },
{ 7, 0, 0, 0, 0 }
};
int result = 0;
while (result == 0)
{
int i = rand5();
int j = rand5();
result = vals[i-1][j-1];
}
return result;
}
它是如何工作的?想象一下:想象一下在纸上打印出这个双维阵列,将它固定在飞镖板上并随意投掷飞镖。如果您达到非零值,则它是1到7之间的统计随机值,因为有相同数量的非零值可供选择。如果你达到零,只要继续投掷飞镖直到你达到非零。这就是这段代码的作用:i和j索引在飞镖板上随机选择一个位置,如果我们没有得到好的结果,我们就会继续投掷飞镖。
像亚当说的那样,在最坏的情况下,这可以永远运行,但从统计上来说,最坏的情况永远不会发生。 :)答案 1 :(得分:342)
没有(完全正确的)解决方案将在恒定的时间内运行,因为1/7是基数为5的无限小数。一个简单的解决方案是使用拒绝采样,例如:
int i;
do
{
i = 5 * (rand5() - 1) + rand5(); // i is now uniformly random between 1 and 25
} while(i > 21);
// i is now uniformly random between 1 and 21
return i % 7 + 1; // result is now uniformly random between 1 and 7
这个循环的预期运行时间为25/21 = 1.19次迭代,但是永远循环的概率极小。
答案 2 :(得分:150)
答案 3 :(得分:36)
(我偷了Adam Rosenfeld's answer并使其运行速度提高了约7%。)
假设rand5()以相等的分布返回{0,1,2,3,4}中的一个,并且目标是返回{0,1,2,3,4,5,6}并具有相等的分布。 / p>
int rand7() {
i = 5 * rand5() + rand5();
max = 25;
//i is uniform among {0 ... max-1}
while(i < max%7) {
//i is uniform among {0 ... (max%7 - 1)}
i *= 5;
i += rand5(); //i is uniform {0 ... (((max%7)*5) - 1)}
max %= 7;
max *= 5; //once again, i is uniform among {0 ... max-1}
}
return(i%7);
}
我们正在跟踪循环在变量max
中可以产生的最大值。如果到目前为止reult在max%7和max-1之间,则结果将在该范围内均匀分布。如果没有,我们使用余数,它在0和最大%7-1之间是随机的,另一次调用rand()来产生一个新的数字和一个新的最大值。然后我们重新开始。
编辑:期望调用rand5()的次数在此等式中为x:
x = 2 * 21/25
+ 3 * 4/25 * 14/20
+ 4 * 4/25 * 6/20 * 28/30
+ 5 * 4/25 * 6/20 * 2/30 * 7/10
+ 6 * 4/25 * 6/20 * 2/30 * 3/10 * 14/15
+ (6+x) * 4/25 * 6/20 * 2/30 * 3/10 * 1/15
x = about 2.21 calls to rand5()
答案 4 :(得分:27)
<强>算法:强>
7可以用3比特的序列表示
使用rand(5)随机填充每个位0或1 例如:call rand(5)和
如果结果为1或2,则用0填充该位 如果结果为4或5,则用1填充该位 如果结果为3,则忽略并再次执行(拒绝)
这样我们可以用0/1随机填充3位,从而得到1-7的数字。
编辑:这似乎是最简单,最有效的答案,所以这里有一些代码:
public static int random_7() {
int returnValue = 0;
while (returnValue == 0) {
for (int i = 1; i <= 3; i++) {
returnValue = (returnValue << 1) + random_5_output_2();
}
}
return returnValue;
}
private static int random_5_output_2() {
while (true) {
int flip = random_5();
if (flip < 3) {
return 0;
}
else if (flip > 3) {
return 1;
}
}
}
答案 5 :(得分:19)
int randbit( void )
{
while( 1 )
{
int r = rand5();
if( r <= 4 ) return(r & 1);
}
}
int randint( int nbits )
{
int result = 0;
while( nbits-- )
{
result = (result<<1) | randbit();
}
return( result );
}
int rand7( void )
{
while( 1 )
{
int r = randint( 3 ) + 1;
if( r <= 7 ) return( r );
}
}
答案 6 :(得分:16)
rand7() = (rand5()+rand5()+rand5()+rand5()+rand5()+rand5()+rand5())%7+1
编辑:这不太奏效。它在1000左右大约2个部分(假设完美的兰德5)。水桶得到:
value Count Error%
1 11158 -0.0035
2 11144 -0.0214
3 11144 -0.0214
4 11158 -0.0035
5 11172 +0.0144
6 11177 +0.0208
7 11172 +0.0144
切换到
的总和n Error%
10 +/- 1e-3,
12 +/- 1e-4,
14 +/- 1e-5,
16 +/- 1e-6,
...
28 +/- 3e-11
似乎每增加2个
就会获得一个数量级顺便说一句:上面的错误表不是通过抽样生成的,而是通过以下递归关系生成的:
p[x,n]
是output=x
n
次rand5
次调用可能发生的数字。
p[1,1] ... p[5,1] = 1
p[6,1] ... p[7,1] = 0
p[1,n] = p[7,n-1] + p[6,n-1] + p[5,n-1] + p[4,n-1] + p[3,n-1]
p[2,n] = p[1,n-1] + p[7,n-1] + p[6,n-1] + p[5,n-1] + p[4,n-1]
p[3,n] = p[2,n-1] + p[1,n-1] + p[7,n-1] + p[6,n-1] + p[5,n-1]
p[4,n] = p[3,n-1] + p[2,n-1] + p[1,n-1] + p[7,n-1] + p[6,n-1]
p[5,n] = p[4,n-1] + p[3,n-1] + p[2,n-1] + p[1,n-1] + p[7,n-1]
p[6,n] = p[5,n-1] + p[4,n-1] + p[3,n-1] + p[2,n-1] + p[1,n-1]
p[7,n] = p[6,n-1] + p[5,n-1] + p[4,n-1] + p[3,n-1] + p[2,n-1]
答案 7 :(得分:15)
int ans = 0;
while (ans == 0)
{
for (int i=0; i<3; i++)
{
while ((r = rand5()) == 3){};
ans += (r < 3) >> i
}
}
答案 8 :(得分:13)
以下在{1,2,3,4,5,6,7}上使用随机数发生器在{1,2,3,4,5}上产生均匀分布产生均匀分布。代码很乱,但逻辑很明确。
public static int random_7(Random rg) {
int returnValue = 0;
while (returnValue == 0) {
for (int i = 1; i <= 3; i++) {
returnValue = (returnValue << 1) + SimulateFairCoin(rg);
}
}
return returnValue;
}
private static int SimulateFairCoin(Random rg) {
while (true) {
int flipOne = random_5_mod_2(rg);
int flipTwo = random_5_mod_2(rg);
if (flipOne == 0 && flipTwo == 1) {
return 0;
}
else if (flipOne == 1 && flipTwo == 0) {
return 1;
}
}
}
private static int random_5_mod_2(Random rg) {
return random_5(rg) % 2;
}
private static int random_5(Random rg) {
return rg.Next(5) + 1;
}
答案 9 :(得分:12)
如果我们考虑额外的约束,试图给出最有效的答案,即给出输入流I
,从1-5输出长度为m
的均匀分布整数的输出流{{ {1}},均匀分布的整数,长度相对于O
的长度为1-7,比如说m
。
分析此问题的最简单方法是将流I和L(m)
分别视为5进制和7进制数。这是通过主答案的想法来实现的,即获取流O
以及类似于流a1, a2, a3,... -> a1+5*a2+5^2*a3+..
。
然后,如果我们将长度为O
的输入流的一部分放在m choose n s.t. 5^m-7^n=c
,并且尽可能小。然后是从长度为m的输入流到从c>0
到1
的整数的统一映射,以及从1到5^m
的整数到长度为n的输出流的另一个统一映射。当映射的整数超过7^n
时,可能必须从输入流中丢失一些情况。
因此,这为约为7^n
的{{1}}提供的值约为L(m)
。
上述分析的难点在于等式m (log5/log7)
,它不容易精确解决,而.82m
到5^m-7^n=c
的统一值超过1
且我们失去了效率。
问题是如何接近m(log5 / log7)的最佳可能值。例如,当这个数接近整数时,我们能找到一种方法来实现这个精确的整数输出值吗?
如果5^m
然后从输入流中我们有效地生成从7^n
到5^m-7^n=c
的统一随机数,并且不使用任何高于0
的值。但是,可以挽救这些值并再次使用。它们有效地生成从1到(5^m)-1
的统一数字序列。因此,我们可以尝试使用它们并将它们转换为7位数,以便我们可以创建更多的输出值。
如果我们让7^n
为5^m-7^n
整数的输出序列的平均长度,该整数是从大小为T7(X)
的统一输入派生的,并假设random(1-7)
。< / p>
然后是X
,因为我们的长度没有序列,概率为7 ^ n0 / 5 ^ m,残差长度为5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7
,概率为T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)
。
如果我们继续代替我们获得:
5^m-7^n0
因此
(5^m-7^n0)/5^m)
另一种说法是:
T7(5^m) = n0x7^n0/5^m + n1x7^n1/5^m + ... + nrx7^nr/5^m = (n0x7^n0 + n1x7^n1 + ... + nrx7^nr)/5^m
最好的情况是我原来的L(m)=T7(5^m)=(n0x7^n0 + n1x7^n1 + ... + nrx7^nr)/(7^n0+7^n1+7^n2+...+7^nr+s)
,其中If 5^m has 7-ary representation `a0+a1*7 + a2*7^2 + a3*7^3+...+ar*7^r
Then L(m) = (a1*7 + 2a2*7^2 + 3a3*7^3+...+rar*7^r)/(a0+a1*7 + a2*7^2 + a3*7^3+...+ar*7^r)
。
然后5^m=7^n+s
和以前一样。
最糟糕的情况是我们只能找到k和s.t 5 ^ m = kx7 + s。
s<7
其他情况介于两者之间。看看我们能为非常大的m做得多好,这将是有趣的,即我们能得到错误项有多好:
T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)
一般来说似乎无法实现Then T7(5^m) = 1x(k.7)/(k.7+s) = 1+o(1)
但希望我们可以证明T7(5^m) = m (Log5/Log7)+e(m)
。
然后整个事情依赖于e(m) = o(1)
的7位数字的分布,用于e(m)=o(m)
的各种值。
我确信有很多理论可以解决这个问题,我可能会在某些方面回顾一下。
答案 10 :(得分:11)
这里是否允许家庭作业?
此函数执行粗略的“基数5”数学运算,以生成0到6之间的数字。
function rnd7() {
do {
r1 = rnd5() - 1;
do {
r2=rnd5() - 1;
} while (r2 > 1);
result = r2 * 5 + r1;
} while (result > 6);
return result + 1;
}
答案 11 :(得分:10)
为什么不这么简单?
int random7() {
return random5() + (random5() % 3);
}
由于模数的原因,在此解决方案中获得1和7的可能性较低,但是,如果您只想要一个快速且可读的解决方案,那么这就是您的选择。
答案 12 :(得分:10)
这是Adam's answer的一个有效的Python实现。
import random
def rand5():
return random.randint(1, 5)
def rand7():
while True:
r = 5 * (rand5() - 1) + rand5()
#r is now uniformly random between 1 and 25
if (r <= 21):
break
#result is now uniformly random between 1 and 7
return r % 7 + 1
我喜欢抛出我正在研究Python的算法,所以我可以玩它们,我想我会在这里发布它,希望它对那里的人有用,而不是花了很长时间才能扔到一起
答案 13 :(得分:9)
int rand7() {
int value = rand5()
+ rand5() * 2
+ rand5() * 3
+ rand5() * 4
+ rand5() * 5
+ rand5() * 6;
return value%7;
}
与所选解决方案不同,算法将在恒定时间内运行。然而,它确实比rand5多调用所选解决方案的平均运行时间。
请注意,此生成器并不完美(数字0的机会比任何其他数字多0.0064%),但对于大多数实际用途,恒定时间的保证可能会超过这种不准确性。
<强>解释强>
这个解决方案源于这样的事实:数字15,624可以被7整除,因此如果我们可以随机均匀地生成从0到15,624的数字,然后取mod 7,我们就可以获得一个接近均匀的rand7生成器。 0到15,624之间的数字可以通过滚动rand5 6次并使用它们形成基数为5的数字来统一生成,如下所示:
rand5 * 5^5 + rand5 * 5^4 + rand5 * 5^3 + rand5 * 5^2 + rand5 * 5 + rand5
mod 7的属性允许我们稍微简化等式:
5^5 = 3 mod 7
5^4 = 2 mod 7
5^3 = 6 mod 7
5^2 = 4 mod 7
5^1 = 5 mod 7
所以
rand5 * 5^5 + rand5 * 5^4 + rand5 * 5^3 + rand5 * 5^2 + rand5 * 5 + rand5
变为
rand5 * 3 + rand5 * 2 + rand5 * 6 + rand5 * 4 + rand5 * 5 + rand5
<强>理论强>
数字15,624不是随机选择的,但可以使用fermat的小定理来发现,该定理指出如果p是素数,那么
a^(p-1) = 1 mod p
所以这给了我们,
(5^6)-1 = 0 mod 7
(5 ^ 6)-1等于
4 * 5^5 + 4 * 5^4 + 4 * 5^3 + 4 * 5^2 + 4 * 5 + 4
这是基数为5的数字,因此我们可以看到此方法可用于从任何随机数生成器转到任何其他随机数生成器。虽然在使用指数p-1时总是会引入朝向0的小偏差。
答案 14 :(得分:8)
假设 rand(n)在这里表示“从 0 到 n-1 的均匀分布中的随机整数”,这是一个代码示例使用Python的randint,它具有这种效果。它仅使用 randint(5)和常量来产生 randint(7)的效果。有点傻,实际上
from random import randint
sum = 7
while sum >= 7:
first = randint(0,5)
toadd = 9999
while toadd>1:
toadd = randint(0,5)
if toadd:
sum = first+5
else:
sum = first
assert 7>sum>=0
print sum
答案 15 :(得分:8)
Adam Rosenfield的正确答案背后的前提是:
当n等于2时,你有4个扔掉的可能性:y = {22,23,24,25}。如果你使用n等于6,你只有1个扔掉:y = {15625}。
5 ^ 6 = 15625
7 * 2232 = 15624
你多次调用rand5。但是,获得丢弃值(或无限循环)的可能性要小得多。如果有办法让y没有可能的丢弃值,我还没有找到它。
答案 16 :(得分:8)
这是我的答案:
static struct rand_buffer {
unsigned v, count;
} buf2, buf3;
void push (struct rand_buffer *buf, unsigned n, unsigned v)
{
buf->v = buf->v * n + v;
++buf->count;
}
#define PUSH(n, v) push (&buf##n, n, v)
int rand16 (void)
{
int v = buf2.v & 0xf;
buf2.v >>= 4;
buf2.count -= 4;
return v;
}
int rand9 (void)
{
int v = buf3.v % 9;
buf3.v /= 9;
buf3.count -= 2;
return v;
}
int rand7 (void)
{
if (buf3.count >= 2) {
int v = rand9 ();
if (v < 7)
return v % 7 + 1;
PUSH (2, v - 7);
}
for (;;) {
if (buf2.count >= 4) {
int v = rand16 ();
if (v < 14) {
PUSH (2, v / 7);
return v % 7 + 1;
}
PUSH (2, v - 14);
}
// Get a number between 0 & 25
int v = 5 * (rand5 () - 1) + rand5 () - 1;
if (v < 21) {
PUSH (3, v / 7);
return v % 7 + 1;
}
v -= 21;
PUSH (2, v & 1);
PUSH (2, v >> 1);
}
}
它比其他人复杂一点,但我相信它最大限度地减少了对rand5的调用。与其他解决方案一样,它可能会循环很长时间。
答案 17 :(得分:7)
简单高效:
int rand7 ( void )
{
return 4; // this number has been calculated using
// rand5() and is in the range 1..7
}
答案 18 :(得分:6)
只要没有可供选择的七种可能性,绘制另一个随机数,将可能性的数量乘以5。在Perl:
$num = 0;
$possibilities = 1;
sub rand7
{
while( $possibilities < 7 )
{
$num = $num * 5 + int(rand(5));
$possibilities *= 5;
}
my $result = $num % 7;
$num = int( $num / 7 );
$possibilities /= 7;
return $result;
}
答案 19 :(得分:6)
我不喜欢从1开始的范围,所以我将从0开始: - )
unsigned rand5()
{
return rand() % 5;
}
unsigned rand7()
{
int r;
do
{
r = rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
r = r * 5 + rand5();
} while (r > 15623);
return r / 2232;
}
答案 20 :(得分:5)
我知道它已被回答,但这似乎工作正常,但我不能告诉你它是否有偏见。我的'测试'表明它至少是合理的。
也许亚当罗森菲尔德很乐意发表评论?
我的(天真的?)想法是这样的:
累积rand5,直到有足够的随机位来生成rand7。这最多需要2兰特5。为了获得rand7号码,我使用累积值mod 7。
为了避免累加器溢出,并且由于累加器是mod 7,那么我采用累加器的mod 7:
(5a + rand5) % 7 = (k*7 + (5a%7) + rand5) % 7 = ( (5a%7) + rand5) % 7
rand7()函数如下:
(我让rand5的范围是0-4,rand7同样是0-6。)
int rand7(){
static int a=0;
static int e=0;
int r;
a = a * 5 + rand5();
e = e + 5; // added 5/7ths of a rand7 number
if ( e<7 ){
a = a * 5 + rand5();
e = e + 5; // another 5/7ths
}
r = a % 7;
e = e - 7; // removed a rand7 number
a = a % 7;
return r;
}
编辑:为1亿次试验添加了结果。
'真实'rand函数mod 5或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
我的rand7
平均看起来不错,数字分布看起来也不错。
randt:avg = 3.000080 0:14288793 1:14280135 2:14287848 3:14285277 4:14286341 5:14278663 6:14292943
答案 21 :(得分:5)
你去,统一发布和零rand5电话。
def rand7:
seed += 1
if seed >= 7:
seed = 0
yield seed
需要事先设定种子。
答案 22 :(得分:4)
这是一个完全适合整数的解决方案,并且在最佳值的4%左右(即{0..4}中每个{0..4}使用1.26随机数)。 Scala中的代码,但数学应该在任何语言中合理清晰:你利用7 ^ 9 + 7 ^ 8非常接近5 ^ 11的事实。因此,您在基数5中选择一个11位数字,然后将其解释为基数7中的9位数字(如果它在范围内(给出9个基数为7的数字)),或者如果它超过9位数字,则将其解释为8位数字等:
abstract class RNG {
def apply(): Int
}
class Random5 extends RNG {
val rng = new scala.util.Random
var count = 0
def apply() = { count += 1 ; rng.nextInt(5) }
}
class FiveSevener(five: RNG) {
val sevens = new Array[Int](9)
var nsevens = 0
val to9 = 40353607;
val to8 = 5764801;
val to7 = 823543;
def loadSevens(value: Int, count: Int) {
nsevens = 0;
var remaining = value;
while (nsevens < count) {
sevens(nsevens) = remaining % 7
remaining /= 7
nsevens += 1
}
}
def loadSevens {
var fivepow11 = 0;
var i=0
while (i<11) { i+=1 ; fivepow11 = five() + fivepow11*5 }
if (fivepow11 < to9) { loadSevens(fivepow11 , 9) ; return }
fivepow11 -= to9
if (fivepow11 < to8) { loadSevens(fivepow11 , 8) ; return }
fivepow11 -= to8
if (fivepow11 < 3*to7) loadSevens(fivepow11 % to7 , 7)
else loadSevens
}
def apply() = {
if (nsevens==0) loadSevens
nsevens -= 1
sevens(nsevens)
}
}
如果将测试粘贴到解释器(实际上是REPL),则会得到:
scala> val five = new Random5
five: Random5 = Random5@e9c592
scala> val seven = new FiveSevener(five)
seven: FiveSevener = FiveSevener@143c423
scala> val counts = new Array[Int](7)
counts: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0)
scala> var i=0 ; while (i < 100000000) { counts( seven() ) += 1 ; i += 1 }
i: Int = 100000000
scala> counts
res0: Array[Int] = Array(14280662, 14293012, 14281286, 14284836, 14287188,
14289332, 14283684)
scala> five.count
res1: Int = 125902876
分布很好且平坦(在每个箱中10 ^ 8的1/7内约10k,正如大约高斯分布所预期的那样)。
答案 23 :(得分:4)
上面引用了优雅的算法,但这是一种接近它的方法,尽管它可能是迂回的。我假设从0生成值。
R2 =随机数生成器给出小于2的值(样本空间= {0,1})
R8 =随机数生成器给出小于8的值(样本空间= {0,1,2,3,4,5,6,7})
为了从R2生成R8,您将运行R2三次,并将所有3次运行的组合结果用作3位二进制数。以下是R2运行三次时的值范围:
0 0 0 - &gt; 0
。
。
1 1 1 - &gt; 7
现在从R8生成R7,如果它返回7,我们只需再次运行R7:
int R7() {
do {
x = R8();
} while (x > 6)
return x;
}
迂回解决方案是从R5生成R2(就像我们从R8生成R7),然后从R2生成R8,然后从R8生成R7。
答案 24 :(得分:3)
这个答案更像是从Rand5函数中获得最大熵的实验。因此,有点不清楚,几乎肯定比其他实现慢得多。
假设0-4的均匀分布并从0-6得到均匀分布:
public class SevenFromFive
{
public SevenFromFive()
{
// this outputs a uniform ditribution but for some reason including it
// screws up the output distribution
// open question Why?
this.fifth = new ProbabilityCondensor(5, b => {});
this.eigth = new ProbabilityCondensor(8, AddEntropy);
}
private static Random r = new Random();
private static uint Rand5()
{
return (uint)r.Next(0,5);
}
private class ProbabilityCondensor
{
private readonly int samples;
private int counter;
private int store;
private readonly Action<bool> output;
public ProbabilityCondensor(int chanceOfTrueReciprocal,
Action<bool> output)
{
this.output = output;
this.samples = chanceOfTrueReciprocal - 1;
}
public void Add(bool bit)
{
this.counter++;
if (bit)
this.store++;
if (counter == samples)
{
bool? e;
if (store == 0)
e = false;
else if (store == 1)
e = true;
else
e = null;// discard for now
counter = 0;
store = 0;
if (e.HasValue)
output(e.Value);
}
}
}
ulong buffer = 0;
const ulong Mask = 7UL;
int bitsAvail = 0;
private readonly ProbabilityCondensor fifth;
private readonly ProbabilityCondensor eigth;
private void AddEntropy(bool bit)
{
buffer <<= 1;
if (bit)
buffer |= 1;
bitsAvail++;
}
private void AddTwoBitsEntropy(uint u)
{
buffer <<= 2;
buffer |= (u & 3UL);
bitsAvail += 2;
}
public uint Rand7()
{
uint selection;
do
{
while (bitsAvail < 3)
{
var x = Rand5();
if (x < 4)
{
// put the two low order bits straight in
AddTwoBitsEntropy(x);
fifth.Add(false);
}
else
{
fifth.Add(true);
}
}
// read 3 bits
selection = (uint)((buffer & Mask));
bitsAvail -= 3;
buffer >>= 3;
if (selection == 7)
eigth.Add(true);
else
eigth.Add(false);
}
while (selection == 7);
return selection;
}
}
每次调用Rand5时添加到缓冲区的位数目前为4/5 * 2,因此为1.6。 如果包含的1/5概率值增加0.05,那么1.65但是请参阅代码中的注释,我必须禁用它。
通过调用Rand7消耗的比特= 3 + 1/8 *(3 + 1/8 *(3 + 1/8 *(...) 这是3 + 3/8 + 3/64 + 3/512 ...所以大约3.42
通过从七人制中提取信息,我每次调用收回1/8 * 1/7位,所以约为0.018
这给每次调用净消耗3.4位,这意味着每个Rand7的比率是对Rand5的2.125调用。最佳值应为2.1。
我认为这种方法显着比其他许多方法慢,除非调用Rand5的成本非常昂贵(比如说要调用一些外部的熵源)。
答案 25 :(得分:3)
通过使用滚动总计,您可以
这两个问题都是简单rand(5)+rand(5)...
类型解决方案的问题。以下Python代码显示了如何实现它(其中大部分证明了分发)。
import random
x = []
for i in range (0,7):
x.append (0)
t = 0
tt = 0
for i in range (0,700000):
########################################
##### qq.py #####
r = int (random.random () * 5)
t = (t + r) % 7
########################################
##### qq_notsogood.py #####
#r = 20
#while r > 6:
#r = int (random.random () * 5)
#r = r + int (random.random () * 5)
#t = r
########################################
x[t] = x[t] + 1
tt = tt + 1
high = x[0]
low = x[0]
for i in range (0,7):
print "%d: %7d %.5f" % (i, x[i], 100.0 * x[i] / tt)
if x[i] < low:
low = x[i]
if x[i] > high:
high = x[i]
diff = high - low
print "Variation = %d (%.5f%%)" % (diff, 100.0 * diff / tt)
此输出显示结果:
pax$ python qq.py
0: 99908 14.27257
1: 100029 14.28986
2: 100327 14.33243
3: 100395 14.34214
4: 99104 14.15771
5: 99829 14.26129
6: 100408 14.34400
Variation = 1304 (0.18629%)
pax$ python qq.py
0: 99547 14.22100
1: 100229 14.31843
2: 100078 14.29686
3: 99451 14.20729
4: 100284 14.32629
5: 100038 14.29114
6: 100373 14.33900
Variation = 922 (0.13171%)
pax$ python qq.py
0: 100481 14.35443
1: 99188 14.16971
2: 100284 14.32629
3: 100222 14.31743
4: 99960 14.28000
5: 99426 14.20371
6: 100439 14.34843
Variation = 1293 (0.18471%)
一个简单的rand(5)+rand(5)
,忽略那些返回超过6的情况,其典型变化为18%, 100次上述方法:
pax$ python qq_notsogood.py
0: 31756 4.53657
1: 63304 9.04343
2: 95507 13.64386
3: 127825 18.26071
4: 158851 22.69300
5: 127567 18.22386
6: 95190 13.59857
Variation = 127095 (18.15643%)
pax$ python qq_notsogood.py
0: 31792 4.54171
1: 63637 9.09100
2: 95641 13.66300
3: 127627 18.23243
4: 158751 22.67871
5: 126782 18.11171
6: 95770 13.68143
Variation = 126959 (18.13700%)
pax$ python qq_notsogood.py
0: 31955 4.56500
1: 63485 9.06929
2: 94849 13.54986
3: 127737 18.24814
4: 159687 22.81243
5: 127391 18.19871
6: 94896 13.55657
Variation = 127732 (18.24743%)
而且,根据Nixuz的建议,我已经清理了脚本,因此您只需提取并使用rand7...
内容:
import random
# rand5() returns 0 through 4 inclusive.
def rand5():
return int (random.random () * 5)
# rand7() generator returns 0 through 6 inclusive (using rand5()).
def rand7():
rand7ret = 0
while True:
rand7ret = (rand7ret + rand5()) % 7
yield rand7ret
# Number of test runs.
count = 700000
# Work out distribution.
distrib = [0,0,0,0,0,0,0]
rgen =rand7()
for i in range (0,count):
r = rgen.next()
distrib[r] = distrib[r] + 1
# Print distributions and calculate variation.
high = distrib[0]
low = distrib[0]
for i in range (0,7):
print "%d: %7d %.5f" % (i, distrib[i], 100.0 * distrib[i] / count)
if distrib[i] < low:
low = distrib[i]
if distrib[i] > high:
high = distrib[i]
diff = high - low
print "Variation = %d (%.5f%%)" % (diff, 100.0 * diff / count)
答案 26 :(得分:3)
在php中
function rand1to7() {
do {
$output_value = 0;
for ($i = 0; $i < 28; $i++) {
$output_value += rand1to5();
}
while ($output_value != 140);
$output_value -= 12;
return floor($output_value / 16);
}
循环生成一个介于16和127之间的随机数,除以十六分之一以创建一个介于1和7.9375之间的浮点数,然后向下舍入以获得介于1和7之间的int。如果我没有记错,则有一个16/112获得7个结果中的任何一个的机会。
答案 27 :(得分:3)
extern int r5();
int r7() {
return ((r5() & 0x01) << 2 ) | ((r5() & 0x01) << 1 ) | (r5() & 0x01);
}
答案 28 :(得分:2)
package CareerCup;
public class RangeTransform {
static int counter = (int)(Math.random() * 5 + 1);
private int func() {
return (int) (Math.random() * 5 + 1);
}
private int getMultiplier() {
return counter % 5 + 1;
}
public int rangeTransform() {
counter++;
int count = getMultiplier();
int mult = func() + 5 * count;
System.out.println("Mult is : " + 5 * count);
return (mult) % 7 + 1;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
RangeTransform rangeTransform = new RangeTransform();
for (int i = 0; i < 35; i++)
System.out.println("Val is : " + rangeTransform.rangeTransform());
}
}
答案 29 :(得分:2)
我们在这里使用约定rand(n) -> [0, n - 1]
从我读过的许多答案来看,它们提供了统一性或停止保证,但不是两者兼而有之(adam rosenfeld第二个答案可能)。
但是,可以这样做。我们基本上有这个分布:
这给我们留下了[0-6]
分布的漏洞:5和6没有
发生概率。想象一下,现在我们试图通过改变它来填补空洞
概率分布和求和。
实际上,我们可以将自己的初始分布转移一个,然后 通过将获得的分布与移动的初始分布相加来重复 两个,然后三个,依此类推,直到7个,不包括在内(我们覆盖了整个范围)。 如下图所示。颜色的顺序,对应于 步骤,是蓝色 - &gt;绿色 - &gt;青色 - &gt;白色 - &gt;品红色 - &gt;黄色 - &gt;红色。
因为每个插槽被7个移位分布中的5个覆盖(移位变化为
0到6),因为我们假设随机数是独立的
ran5()
打电话给另一个人,我们获得
p(x) = 5 / 35 = 1 / 7 for all x in [0, 6]
这意味着,给定来自ran5()
的7个独立随机数,我们可以
计算[0-6]
范围内具有均匀概率的随机数。
实际上,ran5()概率
只要样品是分布,甚至不需要均匀分布
独立(因此从试验到试验的分布保持不变)。
此外,这适用于5和7以外的其他数字。
这为我们提供了以下python函数:
def rand_range_transform(rands):
"""
returns a uniform random number in [0, len(rands) - 1]
if all r in rands are independent random numbers from the same uniform distribution
"""
return sum((x + i) for i, x in enumerate(rands)) % len(rands) # a single modulo outside the sum is enough in modulo arithmetic
可以这样使用:
rand5 = lambda : random.randrange(5)
def rand7():
return rand_range_transform([rand5() for _ in range(7)])
如果我们拨打rand7()
70000次,我们就可以获得:
max: 6 min: 0 mean: 2.99711428571 std: 2.00194697049
0: 10019
1: 10016
2: 10071
3: 10044
4: 9775
5: 10042
6: 10033
这很好,虽然远非完美。事实是,我们的一个假设是 在这个实现中很可能是假的:我们使用PRNG,结果就是这样 下次通话的时间取决于最后一次结果。
也就是说,使用真正随机的数字源,输出也应该是 真的随机。并且该算法在每种情况下终止。
但这需要付费:我们需要为rand5()
拨打7个rand7()
来拨打{{1}}
调用
答案 30 :(得分:2)
此解决方案不会浪费任何熵,并在范围内提供第一个可用的真正随机数。 每次迭代时,未得到答案的概率可以显着降低。在N次迭代中获得答案的概率是0到max(5 ^ N)之间的随机数小于0的概率。该范围内最大的七个倍数(max-max%7)。必须至少迭代两次。但对于所有解决方案而言,这都是必然的。
int random7() {
range = 1;
remainder = 0;
while (1) {
remainder = remainder * 5 + random5() - 1;
range = range * 5;
limit = range - (range % 7);
if (remainder < limit) return (remainder % 7) + 1;
remainder = remainder % 7;
range = range % 7;
}
}
在数字上等同于:
r5=5;
num=random5()-1;
while (1) {
num=num*5+random5()-1;
r5=r5*5;
r7=r5-r5%7;
if (num<r7) return num%7+1;
}
第一个代码以模数形式计算。第二个代码只是简单的数学。或者我在某处弄错了。 : - )
答案 31 :(得分:2)
这是我发现的:
然后我们得到1~7的范围,这是我们正在寻找的Random7。
答案 32 :(得分:2)
假设rand
对所有位赋予相同的权重,则为具有上限的掩码。
int i = rand(5) ^ (rand(5) & 2);
rand(5)
只能返回:1b
,10b
,11b
,100b
,101b
。你只需要关心自己有时设置2位。
答案 33 :(得分:2)
rand25() =5*(rand5()-1) + rand5()
rand7() {
while(true) {
int r = rand25();
if (r < 21) return r%3;
}
}
为什么会这样:循环永远运行的概率为0.
答案 34 :(得分:2)
对于值0-7,您有以下内容:
0 000
1 001
2 010
3 011
4 100
5 101
6 110
7 111
从左到右按位Rand5()有p(1)= {2 / 5,2 / 5,3 / 5}。因此,如果我们补充这些概率分布(~Rand5()),我们应该能够使用它来产生我们的数字。我稍后会尝试用解决方案报告。有人有什么想法吗?
[R
答案 35 :(得分:2)
为什么这不起作用?除了另外一次调用rand5()?
之外i = rand5() + rand5() + (rand5() - 1) //Random number between 1 and 14
i = i % 7 + 1;
答案 36 :(得分:2)
这是我在审核别人的答案后可以创建的最简单的答案:
def r5tor7():
while True:
cand = (5 * r5()) + r5()
if cand < 27:
return cand
cand
在[6,27]范围内,如果r5()的可能结果均匀分布,则可能的结果均匀分布。您可以使用以下代码测试我的答案:
from collections import defaultdict
def r5_outcome(n):
if not n:
yield []
else:
for i in range(1, 6):
for j in r5_outcome(n-1):
yield [i] + j
def test_r7():
d = defaultdict(int)
for x in r5_outcome(2):
s = sum([x[i] * 5**i for i in range(len(x))])
if s < 27:
d[s] += 1
print len(d), d
r5_outcome(2)
生成r5()结果的所有可能组合。我使用与我的解决方案代码中相同的过滤器进行测试。您可以看到所有结果同样可能是因为它们具有相同的值。
答案 37 :(得分:2)
int getOneToSeven(){
int added = 0;
for(int i = 1; i<=7; i++){
added += getOneToFive();
}
return (added)%7+1;
}
答案 38 :(得分:2)
我认为我有四个答案,两个给出精确解like that of @Adam Rosenfield但没有无限循环问题,另外两个几乎完美的解决方案,但实现比第一个更快。
最精确的解决方案需要7次调用rand5
,但我们继续进行以便了解。
Adam的答案强度是它提供了一个完美的均匀分布,并且很有可能(21/25)只需要两次调用rand5()。然而,最坏的情况是无限循环。
下面的第一个解决方案也提供了完美的统一分布,但总共需要42次调用rand5
。没有无限循环。
这是一个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
对于不熟悉R的人,这里有一个简化版本:
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
}
将保留rand5
的分布。如果我们进行数学运算,循环的7次迭代中的每次迭代都有5 ^ 6种可能的组合,因此可能的组合总数为(7 * 5^6) %% 7 = 0
。因此,我们可以将相等组中生成的随机数除以7.有关此问题的更多讨论,请参阅方法二。
以下是所有可能的组合:
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
我认为直接表明亚当的方法运行速度要快得多。在Adam的解决方案中,对rand5
进行42次或更多次调用的概率非常小((4/25)^21 ~ 10^(-17)
)。
现在第二种方法几乎是统一的,但需要6次调用rand5
:
rand7 <- function() (sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
这是一个简化版本:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return r %% 7 + 1
}
这实际上是方法1的一次迭代。如果我们生成所有可能的组合,则得到计数:
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
5^6 = 15625
次试验中会再出现一个号码。
现在,在方法1中,通过添加1到6,我们将数字2233移动到每个连续点。因此,组合的总数将匹配。这是因为5 ^ 6 %% 7 = 1,然后我们做了7个适当的变化,所以(7 * 5 ^ 6 %% 7 = 0)。
如果理解方法1和2的参数,则遵循方法3,并且仅需要对rand5
进行7次调用。此时,我觉得这是精确解决方案所需的最小呼叫次数。
这是一个R实现:
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(1:7, function(i) i * rand5())) %% 7) + 1
对于不熟悉R的人,这里有一个简化版本:
rand7 = function(){
r = 0
for(i in 1:7){
r = r + i * rand5()
}
return r %% 7 + 1
}
将保留rand5
的分布。如果我们进行数学计算,循环的7次迭代中的每次迭代都有5种可能的结果,因此可能的组合总数为(7 * 5) %% 7 = 0
。因此,我们可以将相等组中生成的随机数除以7.请参见方法一和方法二,以便对此进行更多讨论。
以下是所有可能的组合:
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
我认为直接表明亚当的方法仍会跑得更快。在Adam的解决方案中对rand5
进行7次或更多次调用的概率仍然很小((4/25)^3 ~ 0.004
)。
这是第二种方法的微小变化。它几乎是统一的,但需要7次调用rand5
,这是方法2的另一个:
rand7 <- function() (rand5() + sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
这是一个简化版本:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return (r+rand5()) %% 7 + 1
}
如果我们生成所有可能的组合,则会得到计数:
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
在5^7 = 78125
试验中,两个数字会少出现一次。在大多数情况下,我可以忍受。
答案 39 :(得分:2)
function Rand7
put 200 into x
repeat while x > 118
put ((random(5)-1) * 25) + ((random(5)-1) * 5) + (random(5)-1) into x
end repeat
return (x mod 7) + 1
end Rand7
对Rand5的三次调用,平均只在125次中重复6次。
将其视为3D阵列,5x5x5,反复填充1到7,以及6个空白。重新滚动空白。 rand5调用在该数组中创建一个三位数的base-5索引。
使用4D或更高的N维数组会有更少的重复,但这意味着对rand5函数的更多调用成为标准。在更高的维度上,您将开始降低效率回报。在我看来,三个是一个很好的折衷方案,但我没有对它们进行相互测试以确定。这将是特定于rand5的实现。
答案 40 :(得分:2)
只需缩放第一个功能的输出
0) you have a number in range 1-5
1) subtract 1 to make it in range 0-4
2) multiply by (7-1)/(5-1) to make it in range 0-6
3) add 1 to increment the range: Now your result is in between 1-7
答案 41 :(得分:2)
你需要的功能是 rand1_7(),我写了rand1_5(),你可以测试它并绘制它。
import numpy
def rand1_5():
return numpy.random.randint(5)+1
def rand1_7():
q = 0
for i in xrange(7): q+= rand1_5()
return q%7 + 1
答案 42 :(得分:1)
从扩展浮动范围的链接进入此处。这个更有趣。并非得出结论,而是想到给定随机数生成函数f
的基数为 b (在这种情况下为4,我会告诉原因) ,可以将其扩展如下:
(b^0 * f() + b^1 * f() + b^2 * f() .... b^p * f()) / (b^(p+1) - 1) * (b-1)
这会将随机生成器转换为FLOAT生成器。我将在此处b
和p
定义2个参数。尽管这里的“底数”是4,b实际上可以是任何数字,也可以是无理数,等等。p
,我称精度是希望浮点数生成器达到多少粒度的程度。将此视为每个rand5
的呼叫rand7
的呼叫次数。
但是我意识到,如果将b设置为base + 1(在这种情况下为4 + 1 = 5),这是一个最佳选择,您将获得均匀的分布。首先摆脱这种1-5生成器,实际上是rand4()+ 1:
function rand4(){
return Math.random() * 5 | 0;
}
要到达那里,您可以将rand4
替换为rand5()-1
下一步是将rand4从整数生成器转换为浮点生成器
function toFloat(f,b,p){
b = b || 2;
p = p || 3;
return (Array.apply(null,Array(p))
.map(function(d,i){return f()})
.map(function(d,i){return Math.pow(b,i)*d})
.reduce(function(ac,d,i){return ac += d;}))
/
(
(Math.pow(b,p) - 1)
/(b-1)
)
}
这会将我编写的第一个函数应用于给定的rand函数。试试吧:
toFloat(rand4) //1.4285714285714286 base = 2, precision = 3
toFloat(rand4,3,4) //0.75 base = 3, precision = 4
toFloat(rand4,4,5) //3.7507331378299122 base = 4, precision = 5
toFloat(rand4,5,6) //0.2012288786482335 base = 5, precision =6
...
现在,您可以将此浮动范围(0-4 INCLUSIVE)转换为任何其他浮动范围,然后将其降级为整数。这里的基数是4
,因为我们正在处理rand4
,因此值b=5
将为您提供均匀的分布。随着b增长到4以上,您将开始在分布中引入周期性的缺口。我测试了b值,范围从2到8,每个值3000点,并且与JavaScript的本机Math.random相比,在我看来甚至比本机本身更好:
http://jsfiddle.net/ibowankenobi/r57v432t/
对于上面的链接,单击分布顶部的“ bin”按钮以减小合并大小。最后一个图是本机Math.random,第四个图是d = 5是均匀的。
获得浮动范围后,要么乘以7并抛出小数部分,要么乘以7乘以0.5并四舍五入:
((toFloat(rand4,5,6)/4 * 7) | 0) + 1 ---> occasionally you'll get 8 with 1/4^6 probability.
Math.round((toFloat(rand4,5,6)/4 * 7) - 0.5) + 1 --> between 1 and 7
答案 43 :(得分:1)
(rand5() + rand5()) % 7 + 1
Yes, this is effective as it calls rand5() only twice and have O(1) space complexity
考虑rand5()
给出1到5(含)的随机数
(1 + 1)%7 + 1 = 3
(1 + 2)%7 + 1 = 4
(1 + 3)%7 + 1 = 5
(1 + 4)%7 + 1 = 6
(1 + 5)%7 + 1 = 7
(2 + 1)%7 + 1 = 4
(2 + 2)%7 + 1 = 5
(2 + 3)%7 + 1 = 6
(2 + 4)%7 + 1 = 7
(2 + 5)%7 + 1 = 1
...
(5 + 1)%7 + 1 = 7
(5 + 2)%7 + 1 = 1
(5 + 3)%7 + 1 = 2
(5 + 4)%7 + 1 = 3
(5 + 5)%7 + 1 = 4
...
等等
答案 44 :(得分:1)
给定一个产生1到5 rand5()
范围内随机整数的函数,写一个产生1到7范围内随机整数的函数rand7()
在我提出的解决方案中,我只调用rand5
一次
真实解决方案
float rand7()
{
return (rand5() * 7.0) / 5.0 ;
}
这里的分布是按比例缩放的,因此它直接取决于rand5
整数解决方案
int rand7()
{
static int prev = 1;
int cur = rand5();
int r = cur * prev; // 1-25
float f = r / 4.0; // 0.25-6.25
f = f - 0.25; // 0-6
f = f + 1.0; // 1-7
prev = cur;
return (int)f;
}
此处的分配取决于系列rand7(i) ~ rand5(i) * rand5(i-1)
rand7(0) ~ rand5(0) * 1
答案 45 :(得分:1)
这里似乎没有涉及另一个答案:
int rand7() {
int r = 7 / 2;
for (int i = 0; i < 28; i++)
r = ((rand5() - 1) * 7 + r) / 5;
return r + 1;
}
每次迭代r
都是0到6之间的随机值。将其(基数7)附加到0和4之间的随机值(包括0和4),并将结果除以5,得到0到6(包括0和6)范围内的新随机值。 r
以实质性偏见开始(r = 3
非常有偏见!)但每次迭代都会将偏差除以5。
此方法不完全一致;然而,偏见很小。大约1 /(2 ** 64)的东西。这种方法的重要之处在于它具有恒定的执行时间(假设rand5()
也具有恒定的执行时间)。没有任何理论上的担忧,一个不幸的电话可能会永远迭代错误的价值。
此外,还有一个讽刺性的答案(有意或无意,已被覆盖):
1-5已经在1-7范围内,因此以下是有效的实现:
int rand7() {
return rand5();
}
问题没有要求统一分发。
答案 46 :(得分:1)
这是一个利用C ++ 11中的功能的答案
#include <functional>
#include <iostream>
#include <ostream>
#include <random>
int main()
{
std::random_device rd;
unsigned long seed = rd();
std::cout << "seed = " << seed << std::endl;
std::mt19937 engine(seed);
std::uniform_int_distribution<> dist(1, 5);
auto rand5 = std::bind(dist, engine);
const int n = 20;
for (int i = 0; i != n; ++i)
{
std::cout << rand5() << " ";
}
std::cout << std::endl;
// Use a lambda expression to define rand7
auto rand7 = [&rand5]()->int
{
for (int result = 0; ; result = 0)
{
// Take advantage of the fact that
// 5**6 = 15625 = 15624 + 1 = 7 * (2232) + 1.
// So we only have to discard one out of every 15625 numbers generated.
// Generate a 6-digit number in base 5
for (int i = 0; i != 6; ++i)
{
result = 5 * result + (rand5() - 1);
}
// result is in the range [0, 15625)
if (result == 15625 - 1)
{
// Discard this number
continue;
}
// We now know that result is in the range [0, 15624), a range that can
// be divided evenly into 7 buckets guaranteeing uniformity
result /= 2232;
return 1 + result;
}
};
for (int i = 0; i != n; ++i)
{
std::cout << rand7() << " ";
}
std::cout << std::endl;
return 0;
}
答案 47 :(得分:1)
我想你们都在过度思考这个问题。这个简单的解决方案不起作用吗?
int rand7(void)
{
static int startpos = 0;
startpos = (startpos+5) % (5*7);
return (((startpos + rand5()-1)%7)+1);
}
答案 48 :(得分:1)
此解决方案的灵感来自Rob McAfee 但是它不需要循环,结果是均匀分布:
// Returns 1-5
var rnd5 = function(){
return parseInt(Math.random() * 5, 10) + 1;
}
// Helper
var lastEdge = 0;
// Returns 1-7
var rnd7 = function () {
var map = [
[ 1, 2, 3, 4, 5 ],
[ 6, 7, 1, 2, 3 ],
[ 4, 5, 6, 7, 1 ],
[ 2, 3, 4, 5, 6 ],
[ 7, 0, 0, 0, 0 ]
];
var result = map[rnd5() - 1][rnd5() - 1];
if (result > 0) {
return result;
}
lastEdge++;
if (lastEdge > 7 ) {
lastEdge = 1;
}
return lastEdge;
};
// Test the a uniform distribution
results = {}; for(i=0; i < 700000;i++) { var rand = rnd7(); results[rand] = results[rand] ? results[rand] + 1 : 1;}
console.log(results)
结果:[1: 99560, 2: 99932, 3: 100355, 4: 100262, 5: 99603, 6: 100062, 7: 100226]
答案 49 :(得分:1)
这个怎么样
rand5()%2 + rand5()%2 + rand5()%2 + rand5()%2 + rand5()%2 + rand5()%2
不确定这是统一分布的。有什么建议吗?
答案 50 :(得分:1)
这与@RobMcAfee类似,只是我使用幻数而不是2维数组。
int rand7() {
int m = 1203068;
int r = (m >> (rand5() - 1) * 5 + rand5() - 1) & 7;
return (r > 0) ? r : rand7();
}
答案 51 :(得分:1)
function rand7() {
while (true) { //lowest base 5 random number > 7 reduces memory
int num = (rand5()-1)*5 + rand5()-1;
if (num < 21) // improves performance
return 1 + num%7;
}
}
Python代码:
from random import randint
def rand7():
while(True):
num = (randint(1, 5)-1)*5 + randint(1, 5)-1
if num < 21:
return 1 + num%7
100000次运行的测试分布:
>>> rnums = []
>>> for _ in range(100000):
rnums.append(rand7())
>>> {n:rnums.count(n) for n in set(rnums)}
{1: 15648, 2: 15741, 3: 15681, 4: 15847, 5: 15642, 6: 15806, 7: 15635}
答案 52 :(得分:1)
这里有很多解决方案没有产生统一的分布,许多评论指出了这一点,但问题没有说明这是一个要求。最简单的解决方案是:
int rand_7() { return rand_5(); }
1到5范围内的随机整数显然在1到7的范围内。从技术上讲,最简单的解决方案是返回常量,但这太简单了。
但是,我认为rand_5函数的存在是一个红色的鲱鱼。假设该问题被称为“产生均匀分布的伪随机数发生器,其整数输出在1-7范围内”。这是一个简单的问题(技术上并不简单,但已经解决了,所以你可以查一查。)
另一方面,如果问题被解释为意味着你实际上有一个真正的随机数生成器用于1 - 5范围内的整数(非伪随机),那么解决方案是:
1) examine the rand_5 function
2) understand how it works
3) profit
答案 53 :(得分:1)
这是我的一般实现,在[0,B-1]范围内给出均匀生成器的情况下,在[0,N-1]范围内生成均匀。
public class RandomUnif {
public static final int BASE_NUMBER = 5;
private static Random rand = new Random();
/** given generator, returns uniform integer in the range 0.. BASE_NUMBER-1
public static int randomBASE() {
return rand.nextInt(BASE_NUMBER);
}
/** returns uniform integer in the range 0..n-1 using randomBASE() */
public static int randomUnif(int n) {
int rand, factor;
if( n <= 1 ) return 0;
else if( n == BASE_NUMBER ) return randomBASE();
if( n < BASE_NUMBER ) {
factor = BASE_NUMBER / n;
do
rand = randomBASE() / factor;
while(rand >= n);
return rand;
} else {
factor = (n - 1) / BASE_NUMBER + 1;
do {
rand = factor * randomBASE() + randomUnif(factor);
} while(rand >= n);
return rand;
}
}
}
效率不高,但一般而紧凑。对基础生成器的平均调用:
n calls
2 1.250
3 1.644
4 1.252
5 1.000
6 3.763
7 3.185
8 2.821
9 2.495
10 2.250
11 3.646
12 3.316
13 3.060
14 2.853
15 2.650
16 2.814
17 2.644
18 2.502
19 2.361
20 2.248
21 2.382
22 2.277
23 2.175
24 2.082
25 2.000
26 5.472
27 5.280
28 5.119
29 4.899
答案 54 :(得分:1)
int rand7()
{
return ( rand5() + (rand5()%3) );
}
答案 55 :(得分:1)
首先我想到的是这个。但我不知道它是否均匀分布。 在python中实现
导入随机
def rand5():
return random.randint(1,5)
def rand7():
return(((rand5() - 1)* rand5())%7)+1
答案 56 :(得分:1)
我想到了这个问题的一个有趣的解决方案,并想分享它。
function rand7() {
var returnVal = 4;
for (var n=0; n<3; n++) {
var rand = rand5();
if (rand==1||rand==2){
returnVal+=1;
}
else if (rand==3||rand==4) {
returnVal-=1;
}
}
return returnVal;
}
我构建了一个测试函数,它循环遍历rand7()10,000次,总结所有返回值,并将其除以10,000。如果rand7()工作正常,我们的计算平均值应为4 - 例如,(1 + 2 + 3 + 4 + 5 + 6 + 7/7)= 4.进行多次测试后,平均值确实为4 :)
答案 57 :(得分:0)
对于[1,5]到[1,7]范围,这相当于将7面模具与5面模具一起滚动。
但是,如果没有“浪费”随机性(或者在最坏的情况下永远运行),就不可能做到这一点,因为所有7的质数因子(即7)都不除以5。因此,可以做到的最好要做的就是使用剔除采样来任意地接近于没有“浪费”的随机性(例如通过分批注入5面模具,直到5 ^ n 足够接近) 7)。该问题的解决方案已在其他答案中给出。
更一般而言,将具有 p 面的骰子滚动到具有 k 面的骰子的算法将不可避免地“浪费”随机性(并在最坏的情况下永远运行)除非B. Kloeckner在“ Simulating a dice with a dice”中的引理3指出,“除以质数除以 k 的每个基元也除以 p 的基元”。例如,以更实际的情况为例, p 是2的幂,而 k 是任意的。在这种情况下,除非 k 也是2的幂,否则这种“浪费”和不确定的运行时间是不可避免的。
答案 58 :(得分:0)
此表达式足以获得1到7之间的随机整数
int j = ( rand5()*2 + 4 ) % 7 + 1;
答案 59 :(得分:0)
rand5()=> [1-5]
rand5()%2 => [0-2]
rand5()+ rand5()%2 => [1-7]
rand7() {
return rand5()+rand5()%2;
}
答案 60 :(得分:0)
这是一个解决方案,它试图使对rand5()的调用次数最小化,同时保持实现的简单性和有效性。特别是,与Adam Rosenfield的第二个答案不同,它不需要任意大整数。它利用了23/19 = 1.21052 ...是log(7)/ log(5)= 1.20906 ...的良好有理近似这一事实,因此我们可以生成{1,...,7 }中{1,...,5}的23个随机元素中的}通过拒绝采样而具有很小的拒绝概率。平均而言,以下算法对于每个对rand7()的调用大约需要1.266次对rand5()的调用。如果rand5()的分布是均匀的,则rand7()也是。
uint_fast64_t pool;
int capacity = 0;
void new_batch (void)
{
uint_fast64_t r;
int i;
do {
r = 0;
for (i = 0; i < 23; i++)
r = 5 * r + (rand5() - 1);
} while (r >= 11398895185373143ULL); /* 7**19, a bit less than 5**23 */
pool = r;
capacity = 19;
}
int rand7 (void)
{
int r;
if (capacity == 0)
new_batch();
r = pool % 7;
pool /= 7;
capacity--;
return r + 1;
}
答案 61 :(得分:0)
该算法将rand5的调用次数减少到理论上的最小值7/5。通过产生接下来的5个rand7号码来调用它7次。
没有拒绝任何随机位,并且没有可能一直等待结果。
#!/usr/bin/env ruby
# random integer from 1 to 5
def rand5
STDERR.putc '.'
1 + rand( 5 )
end
@bucket = 0
@bucket_size = 0
# random integer from 1 to 7
def rand7
if @bucket_size == 0
@bucket = 7.times.collect{ |d| rand5 * 5**d }.reduce( &:+ )
@bucket_size = 5
end
next_rand7 = @bucket%7 + 1
@bucket /= 7
@bucket_size -= 1
return next_rand7
end
35.times.each{ putc rand7.to_s }
答案 62 :(得分:0)
与Martin's answer类似,但更不频繁地抛弃熵:
int rand7(void) {
static int m = 1;
static int r = 0;
for (;;) {
while (m <= INT_MAX / 5) {
r = r + m * (rand5() - 1);
m = m * 5;
}
int q = m / 7;
if (r < q * 7) {
int i = r % 7;
r = r / 7;
m = q;
return i + 1;
}
r = r - q * 7;
m = m - q * 7;
}
}
这里我们在0
和m-1
之间建立一个随机值,并尝试通过添加尽可能多的状态来最大化m
而不会溢出(INT_MAX
是最适合C中int
的值,或者您可以将其替换为您的语言和架构中有意义的任何大值。
然后;如果r
落在可被7整除的最大可能区间内,那么它包含一个可行的结果,我们可以将该区间除以7,并将余数作为结果,并将剩余的值返回到我们的熵池。否则r
在另一个不均匀分配的区间内,我们必须从那个不合适的区间中丢弃并重新启动我们的熵池。
与此处的热门答案相比,它平均调用rand5()
的频率约为一半。
这些分歧可以分解成琐碎的比特和LUT用于表现。
答案 63 :(得分:0)
这个问题的主要概念是关于正态分布,这里提供了一个简单的递归解决方案来解决这个问题
假设我们的范围已经有rand5()
:
def rand7():
# twoway = 0 or 1 in the same probability
twoway = None
while not twoway in (1, 2):
twoway = rand5()
twoway -= 1
ans = rand5() + twoway * 5
return ans if ans in range(1,8) else rand7()
我们可以将这个程序分成两部分:
twoway
中有1或2 ans
复合rand5() + twoway * 5
,这正是rand10()
的结果,如果这与我们的需要不匹配(1~7),那么我们再次运行rand7。 P.S。我们不能直接在第二部分中运行while循环,因为twoway
的每个概率都需要是个体的。
但是有一个权衡,因为第一部分中的while循环和return语句中的递归,这个函数不保证执行时间,实际上没有效果。
我做了一个简单的测试来观察我的答案的分布。
result = [ rand7() for x in xrange(777777) ]
ans = {
1: 0,
2: 0,
3: 0,
4: 0,
5: 0,
6: 0,
7: 0,
}
for i in result:
ans[i] += 1
print ans
它给了
{1: 111170, 2: 110693, 3: 110651, 4: 111260, 5: 111197, 6: 111502, 7: 111304}
因此我们可以知道这个答案是正常分布的。
如果你不关心这个功能的执行时间,这里是基于我给出的上述答案的简化答案:
def rand7():
ans = rand5() + (rand5()-1) * 5
return ans if ans < 8 else rand7()
这增加了大于8的价值概率,但可能是这个问题的最短答案。
答案 64 :(得分:0)
这是我的,这会尝试从多个Math.random()
函数调用中重新创建rand5()
,通过使用“加权分数”重新构造单位间隔(Math.random()
的输出范围) (?)。然后使用此随机单位间隔生成1到7之间的随机整数:
function rand5(){
return Math.floor(Math.random()*5)+1;
}
function rand7(){
var uiRandom=0;
var div=1;
for(var i=0; i<7; i++){
div*=5;
var term=(rand5()-1)/div;
uiRandom+=term;
}
//return uiRandom;
return Math.floor(uiRandom*7)+1;
}
要解释:我们采用0-4之间的随机整数(仅rand5()-1
)并将每个结果乘以1 / 5,1 / 25,1 / 125,...然后将它们相加。它类似于二进制加权分数的工作方式;我想相反,我们将其称为quinary(base-5)加权分数:从0 - 0.999999生成一个数字,作为一系列(1/5)^ n项。
修改函数以获取任何输入/输出随机整数范围应该是微不足道的。并且在重写为闭包时可以优化上面的代码。
或者,我们也可以这样做:
function rand5(){
return Math.floor(Math.random()*5)+1;
}
function rand7(){
var buffer=[];
var div=1;
for (var i=0; i<7; i++){
buffer.push((rand5()-1).toString(5));
div*=5;
}
var n=parseInt(buffer.join(""),5);
var uiRandom=n/div;
//return uiRandom;
return Math.floor(uiRandom*7)+1;
}
我们实际上是在制作一个五元数并将它变成一个分数(0--0.9999 ......和以前一样),而不是摆弄构造一个五元(基数为5)的加权分数,然后计算我们的随机数1- -7位数。
以上结果(代码段#2:每次运行100,000次调用):
1:14263; 2:14414; 3:14249; 4:14109; 5:14217; 6:14361; 7:14387
1:14205; 2:14394; 3:14238; 4:14187; 5:14384; 6:14224; 7:14368
1:14425; 2:14236; 3:14334; 4:14232; 5:14160; 6:14320; 7:14293
答案 65 :(得分:0)
首先,我将ramdom5()在1点上移动6次,得到7个随机数。 其次,我添加7个数字以获得共同的总和。 第三,我在7岁的时候得到了师的其余部分。 最后,我添加1以获得从1到7的结果。 这种方法给出了从1到7范围内获得数字的相等概率,但1除外.1的概率略高。
public int random7(){
Random random = new Random();
//function (1 + random.nextInt(5)) is given
int random1_5 = 1 + random.nextInt(5); // 1,2,3,4,5
int random2_6 = 2 + random.nextInt(5); // 2,3,4,5,6
int random3_7 = 3 + random.nextInt(5); // 3,4,5,6,7
int random4_8 = 4 + random.nextInt(5); // 4,5,6,7,8
int random5_9 = 5 + random.nextInt(5); // 5,6,7,8,9
int random6_10 = 6 + random.nextInt(5); //6,7,8,9,10
int random7_11 = 7 + random.nextInt(5); //7,8,9,10,11
//sumOfRandoms is between 28 and 56
int sumOfRandoms = random1_5 + random2_6 + random3_7 +
random4_8 + random5_9 + random6_10 + random7_11;
//result is number between 0 and 6, and
//equals 0 if sumOfRandoms = 28 or 35 or 42 or 49 or 56 , 5 options
//equals 1 if sumOfRandoms = 29 or 36 or 43 or 50, 4 options
//equals 2 if sumOfRandoms = 30 or 37 or 44 or 51, 4 options
//equals 3 if sumOfRandoms = 31 or 38 or 45 or 52, 4 options
//equals 4 if sumOfRandoms = 32 or 39 or 46 or 53, 4 options
//equals 5 if sumOfRandoms = 33 or 40 or 47 or 54, 4 options
//equals 6 if sumOfRandoms = 34 or 41 or 48 or 55, 4 options
//It means that the probabilities of getting numbers between 0 and 6 are almost equal.
int result = sumOfRandoms % 7;
//we should add 1 to move the interval [0,6] to the interval [1,7]
return 1 + result;
}
答案 66 :(得分:0)
如果有人可以给我反馈,那会很酷,我使用JUNIT而没有断言模式,因为在Eclipse中运行它很容易,也很快,我也可以定义一个主方法。顺便说一句,我假设rand5给出0-4的值,加1会使它成为1-5,与rand7相同......所以讨论应该是解决方案,它的分布,而不是它的进展从0-4或1-5 ...
package random;
import java.util.Random;
import org.junit.Test;
public class RandomTest {
@Test
public void testName() throws Exception {
long times = 100000000;
int indexes[] = new int[7];
for(int i = 0; i < times; i++) {
int rand7 = rand7();
indexes[rand7]++;
}
for(int i = 0; i < 7; i++)
System.out.println("Value " + i + ": " + indexes[i]);
}
public int rand7() {
return (rand5() + rand5() + rand5() + rand5() + rand5() + rand5() + rand5()) % 7;
}
public int rand5() {
return new Random().nextInt(5);
}
}
当我运行它时,我得到了这个结果:
Value 0: 14308087
Value 1: 14298303
Value 2: 14279731
Value 3: 14262533
Value 4: 14269749
Value 5: 14277560
Value 6: 14304037
这似乎是一个非常公平的分布,不是吗?
如果我添加rand5()的次数少于或多次(其中次数不能被7整除),则分布会清楚地显示偏移量。例如,添加rand5()
3次:
Value 0: 15199685
Value 1: 14402429
Value 2: 12795649
Value 3: 12796957
Value 4: 14402252
Value 5: 15202778
Value 6: 15200250
所以,这将导致以下结果:
public int rand(int range) {
int randomValue = 0;
for(int i = 0; i < range; i++) {
randomValue += rand5();
}
return randomValue % range;
}
然后,我可以走得更远:
public static final int ORIGN_RANGE = 5;
public static final int DEST_RANGE = 7;
@Test
public void testName() throws Exception {
long times = 100000000;
int indexes[] = new int[DEST_RANGE];
for(int i = 0; i < times; i++) {
int rand7 = convertRand(DEST_RANGE, ORIGN_RANGE);
indexes[rand7]++;
}
for(int i = 0; i < DEST_RANGE; i++)
System.out.println("Value " + i + ": " + indexes[i]);
}
public int convertRand(int destRange, int originRange) {
int randomValue = 0;
for(int i = 0; i < destRange; i++) {
randomValue += rand(originRange);
}
return randomValue % destRange;
}
public int rand(int range) {
return new Random().nextInt(range);
}
我尝试用各种值替换destRange和originRange(ORIGIN为7,DEST为13),我得到了这个分布:
Value 0: 7713763
Value 1: 7706552
Value 2: 7694697
Value 3: 7695319
Value 4: 7688617
Value 5: 7681691
Value 6: 7674798
Value 7: 7680348
Value 8: 7685286
Value 9: 7683943
Value 10: 7690283
Value 11: 7699142
Value 12: 7705561
我可以从这里得出的结论是,你可以通过将起始随机和#34;目的地&#34;相加来将任何随机变为任意。倍。这将获得一种高斯分布(更可能是中间值,边缘值更不常见)。然而,目的地模数似乎在这个高斯分布上均匀分布......从数学家那里获得反馈会很棒......
很酷的是,成本是100%可预测和恒定的,而其他解决方案导致无限循环的概率很小......
答案 67 :(得分:0)
已经很好地解决了这个简单的解决方案:为一个random5
结果取两个random7
个样本,如果结果超出生成均匀分布的范围,则执行此操作。如果您的目标是减少拨打random5
的次数,这非常浪费 - 每个random5
输出的random7
的平均呼叫数为2.38,而不是2,因为扔掉样品。
您可以通过使用更多random5
输入一次生成多个random7
输出来做得更好。对于使用31位整数计算的结果,当使用12次调用random5
生成9 random7
次输出时,最佳值为:每次输出平均为1.34次调用。这是有效的,因为244140625结果中只有2018983需要报废,或者不到1%。
Python演示:
def random5():
return random.randint(1, 5)
def random7gen(n):
count = 0
while n > 0:
samples = 6 * 7**9
while samples >= 6 * 7**9:
samples = 0
for i in range(12):
samples = samples * 5 + random5() - 1
count += 1
samples //= 6
for outputs in range(9):
yield samples % 7 + 1, count
samples //= 7
count = 0
n -= 1
if n == 0: break
>>> from collections import Counter
>>> Counter(x for x,i in random7gen(10000000))
Counter({2: 1430293, 4: 1429298, 1: 1428832, 7: 1428571, 3: 1428204, 5: 1428134, 6: 1426668})
>>> sum(i for x,i in random7gen(10000000)) / 10000000.0
1.344606
答案 68 :(得分:0)
你为什么不将5除以7然后再乘以? (当然,你必须使用浮点数no.s)
它比其他解决方案更容易,更可靠(真的吗?)。例如。在Python中:
def ranndomNo7():
import random
rand5 = random.randint(4) # Produces range: [0, 4]
rand7 = int(rand5 / 5 * 7) # /5, *7, +0.5 and floor()
return rand7
不是那么容易吗?
答案 69 :(得分:-1)
def rand5():
return random.randint(1,5) #return random integers from 1 to 5
def rand7():
rand = rand5()+rand5()-1
if rand > 7: #if numbers > 7, call rand7() again
return rand7()
print rand%7 + 1
我想这是最简单的解决方案,但是人们在http://www.geeksforgeeks.org/generate-integer-from-1-to-7-with-equal-probability/中建议5*rand5() + rand5() - 5
。
有人可以解释rand5()+rand5()-1
答案 70 :(得分:-1)
在所有这些复杂的答案面前我感到愚蠢。
为什么不能:
int random1_to_7()
{
return (random1_to_5() * 7) / 5;
}
答案 71 :(得分:-1)
// returns random number between 0-5 with equal probability
function rand5() {
return Math.floor(Math.random() * 6);
}
// returns random number between 0-7 with equal probability
function rand7() {
if(rand5() % 2 == 0 && rand5() % 2 == 0) {
return 6 + rand5() % 2;
} else {
return rand5();
}
}
console.log(rand7());
答案 72 :(得分:-2)
产生近似均匀分布的恒定时间解决方案。 诀窍是625恰好被7完全整除,你可以在你建立到那个范围时获得统一的分布。
编辑:我的不好,我算错了,但不要拉它,我会留下它以防有人发现它有用/有趣。毕竟确实工作......:)
int rand5()
{
return (rand() % 5) + 1;
}
int rand25()
{
return (5 * (rand5() - 1) + rand5());
}
int rand625()
{
return (25 * (rand25() - 1) + rand25());
}
int rand7()
{
return ((625 * (rand625() - 1) + rand625()) - 1) % 7 + 1;
}
答案 73 :(得分:-2)
这是我想出的答案,但是这些复杂的答案让我觉得这完全不可行/:))
import random
def rand5():
return float(random.randint(0,5))
def rand7():
random_val = rand5()
return float(random.randint((random_val-random_val),7))
print rand7()
答案 74 :(得分:-3)
#!/usr/bin/env ruby
class Integer
def rand7
rand(6)+1
end
end
def rand5
rand(4)+1
end
x = rand5() # x => int between 1 and 5
y = x.rand7() # y => int between 1 and 7
..虽然这可能被视为作弊..
答案 75 :(得分:-3)
int rand7()
{
int zero_one_or_two = ( rand5() + rand5() - 1 ) % 3 ;
return rand5() + zero_one_or_two ;
}
答案 76 :(得分:-4)
执行此操作的标准方法如下:
/**
* Returns a pseudo-random number between min and max, inclusive.
* The difference between min and max can be at most
* <code>Integer.MAX_VALUE - 1</code>.
*
* @param min Minimum value
* @param max Maximum value. Must be greater than min.
* @return Integer between min and max, inclusive.
* @see java.util.Random#nextInt(int)
*/
public static int randInt(int min, int max) {
// NOTE: Usually this should be a field rather than a method
// variable so that it is not re-seeded every call.
Random rand = new Random();
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = rand.nextInt((max - min) + 1) + min;
return randomNum;
}
传递最小值最大值此函数将返回最小值
之间的值答案 77 :(得分:-4)
解决方案
<?php
function random_5(){
return rand(1,5);
}
function random_7(){
$total = 0;
for($i=0;$i<7;$i++){
$total += random_5();
}
return ($total%7)+1;
}
echo random_7();
?>
答案 78 :(得分:-4)
我玩过了,我为这个Rand(7)算法写了"testing environment"。例如,如果您想尝试分配给出算法的分布或生成所有不同随机值所需的迭代次数(对于Rand(7)1-7),则为can use it。
我的核心算法是:
return (Rand5() + Rand5()) % 7 + 1;
与亚当罗森菲尔德的那个一样,分布均匀。 (which I included in my snippet code)
private static int Rand7WithRand5()
{
//PUT YOU FAVOURITE ALGORITHM HERE//
//1. Stackoverflow winner
int i;
do
{
i = 5 * (Rand5() - 1) + Rand5(); // i is now uniformly random between 1 and 25
} while (i > 21);
// i is now uniformly random between 1 and 21
return i % 7 + 1;
//My 2 cents
//return (Rand5() + Rand5()) % 7 + 1;
}
这个“测试环境”可以采用任何Rand(n)算法并测试和评估它(分布和速度)。只需将您的代码放入“Rand7WithRand5”方法并运行代码段即可。
很少有观察结果:
random.Next(1, 8)
)已完成,因为它在大约200多次迭代中以给定间隔生成所有成员,Rand7WithRand5算法需要10k(约30-70k)