在这段代码中,哪里生成了一个随机数?

3
我发现rgba的浮点随机数生成器:

http://rgba.org/articles/sfrand/sfrand.htm

说明很清晰,代码很简单。但有一个问题:我无法弄清楚随机位是从哪里来的。以下是代码片段:
static unsigned int mirand = 1;

float sfrand( void )
{
    unsigned int a;
    mirand *= 16807;
    a = (mirand&0x007fffff) | 0x40000000;
    return( *((float*)&a) - 3.0f );
}

我尝试编译这个函数,总是得到-0.995993的结果,如果mirand最初为1,那么这是有道理的。 我错过了什么,或者mirand只是种子?

1
它并不总是返回相同的数字。如果你连续调用它几次,你会发现它每次返回的数字都不同。当然,每次运行程序时,这个序列都将是相同的。 - Gabe
@Gabe:起初,我在codepad.org、ideone.com和自己的机器上进行了编译。两个网站给出的结果都是-0.995993,而ideone.com给出的结果为-3(这是错误的)。我现在已经多次运行了这个函数,@aleph_null的答案帮助我理解了使用意图。 - Xavier Ho
你是说如果在循环中运行该函数,每次都会得到相同的结果? - Gabe
不,我在循环中运行了该函数,每次迭代的结果都不同。http://codepad.org/d96GzyBV - Xavier Ho
好的,那么每次运行函数时你就不会得到相同的结果。 - Gabe
2个回答

6
事实上,代码并不能生成随机数......它生成的是伪随机数。假设你使用相同的种子开始,一个外行人可以想象正在发生的事情就是你只是在穿过一个巨大的“随机”数字列表。最终,这些数字会重复出现,但是一个好的公式会使得这个重复发生在很长的间隔内。
为了澄清,看一下Excel使用的rand公式:http://support.microsoft.com/kb/828795

很有道理!谢谢。我刚在这里验证了你的答案:http://codepad.org/d96GzyBV 当计时器结束时,我会接受你的答案。:] - Xavier Ho

1
你每次得到相同的数字是因为这实际上是一个伪随机数生成器,就像常规的随机数生成器一样。它没有种子,所以在第n次调用时总是会得到相同的值。如果您连续调用sfrand几次,您将看到结果,这些结果在-1和1之间均匀分布:
-0.995993
0.347347
-0.130602
0.970146
-0.749159
0.883045

就像如果您调用未进行种子处理的普通rand()函数一样,您将看到相同的序列。正如讨论过的那样,选择数字16807是有充分理由的,因此您可以通过调用该函数随机次数来种植这个数字:

static unsigned int mirand = 1;

float sfrand(double seed)
{
    unsigned int a;
    mirand *= seed;
    a = (mirand&0x007fffff) | 0x40000000;
    return( *((float*)&a) - 3.0f );
}

int main()
{
    srand(time(NULL));
    int count = rand() % 1000 + 1
    for(int i = 0; i < count; ++i)
        sfrand();
}

这将仅丢弃前count个值,同时仍然提供随机种子,所有后续调用仍将获得函数旨在的性能提升。随后的调用现在返回唯一值:

codys-macbook-pro:~ cody$ ./a.out
0.166836
codys-macbook-pro:~ cody$ ./a.out
0.256372
codys-macbook-pro:~ cody$ ./a.out
-0.194259
codys-macbook-pro:~ cody$ ./a.out
-0.556834

实际上,您可能需要重新阅读这篇文章。他们有充分的理由精选了“16807”。不过,您的解释与我们的理解一致。:] - Xavier Ho
1
他们这样做了,我会编辑我的答案,包括一个可用的随机种子。 - Cody
嗯,有趣。虽然我更喜欢使用不同的种子生成不同的序列,但丢弃前n个值确实是合乎逻辑的,尽管如你所说,最初会受到一些开销的影响。 - Xavier Ho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接