Delphi中的随机函数有多可靠?

12

我正在用Delphi编写一个撰写统计测试的程序(必须使用Delphi),听说Random功能有些奇怪。在程序启动时,必须调用randomize来随机化随机函数的种子。

我想知道在调用randomize后,random函数是否足够随机以进行统计测试,或者是否需要使用Mersenne Twister?是否有人了解随机数的实际实现情况,能告诉我这一点有多重要?


4
使用随机数生成器函数时需要进行种子设置并不罕见 - 使用Mersenne twister时也需要进行相同的操作。 - Michael Madsen
1
而且,有可能种子随机函数通常是一个巨大的好处。通过使用相同的种子,您可以复制您的结果,这在调试时非常有帮助。 - Svein Bringsli
1
Julian Bucknall的书《Delphi大全:算法与数据结构》(www.boyet.com)第6章中有关于这个主题的精彩讨论。 - MikeJ-UK
9个回答

20

Delphi的伪随机数生成器(PRNG)像几乎所有编程语言RTL PRNG一样,是线性同余生成器

对于大多数小规模应用来说,这已经足够好了,但也需要注意一些问题。特别是要注意低位比特:乘法和加法的模式意味着低位比特并不是非常随机。但这个问题通常仅适用于从中提取出来并使用mod或类似方法截断的大型32位值。在内部使用Random(10)提取0到9之间的值时,将在整个32位范围内执行乘法而不是mod运算。


17

alt text

我忍不住了。


2
我喜欢幽默。但它必须是 CW! - Andreas Rejbrand
1
不,没有连续波,它完美地说明了我们可能认为的非随机实际上可以是完全随机的。毕竟,随机并不意味着没有模式... - Marjan Venema
1
每当你感到想在你的“回答”中加入“我忍不住”的时候,你应该倾向于将其变成 CW。这是一个程度问题,但在这种情况下,没有直接回答 OP 的问题。 - Argalatyr
2
我不介意别人对此进行负面评价。我没有将其变成社区维基的权力。投赞/踩,我真的不在意……我并不试图操纵SO的声誉系统。 - Mick

6

6
无论在什么情况下,Random是否足够可靠取决于您打算使用它的上下文环境。
话虽如此,我已经编写了几个需要进行正确统计的Delphi代码,并使用Random来获取各种空值分布、数据伪复制和重新采样。到目前为止,我在自己的代码中没有遇到过任何情况,其中Random会产生偏倚或不可靠的结果,或者结果会排除其用于预期的统计测试。但是,适用于我的代码的内容不一定适用于您的代码。
如果有疑问,您当然可以对调用Random的结果进行统计分析(例如在R、SPSS等软件中),并检查结果的分布是否违反您特定统计测试的分布要求。(如果您是一名合格的科学家,这本来就是您应该做的。)
如果需要其他PRNGs-例如TPMath库包含一些。(对于更复杂的事情,还可以通过Delphi从R中调用精细的统计函数。)

4
除非您购买某些相对晦涩的硬件,否则计算机可以提供的最好的随机数近似值是完全确定性的伪随机序列。一般来说,randomize函数使用某些相对随机的值(通常基于时间,但有时基于鼠标移动 - 我不知道Delphi做什么)作为种子,提供伪随机序列的入口点。如果没有这个,每次都会获得相同顺序的随机数集,这往往会使使用随机数失去意义。
好吧,我意识到这并没有回答关于可靠性的问题,但这应该让您对要求调用randomize是一个好生成器的迹象而不是一个坏生成器有一定信心。有一堆统计测试显示数字序列的随机程度,而且Delphi随机数生成器很可能适用于许多目的,因为它是一个成熟的产品。

3
只是想增加可能性 - Windows提供了一系列内置的密码学函数。如果默认情况下没有包含它们,那么很可能有一个Delphi封装程序。

在这些函数中,还有一个具有密码学强度的随机数生成器。这是软件中获得的最好的随机性,因为它基于非常长的因素列表进行自我种子化。我不确定,但我怀疑它甚至会使用硬件随机数生成器(如果您有)。

如果这还不够,您还可以尝试注册Quantum Random Bit Generator Service以获取一些真正的随机值。


2

从 Embarcadero 网站上得知:

_lrand 是长随机数生成函数。_rand 使用一个乘法同余随机数生成器,周期为 2^64,以返回在范围从 0 到 2^31 - 1 的连续伪随机数。

通过使用 srand 函数并传入参数值 1 来重新初始化生成器。可以通过使用给定的种子数字调用 srand 函数来将其设置为新的起始点。


2
如果自从我分析它以来(大约是Delphi 4),他们没有改变实现方式,那么Delphi PRNG的实现方式如下:
Randseed:=int32(Randseed*$08088405)+1
result:=Randseed*Range shr 32

(伪代码/假设乘法是在任意大的整数上进行)


-2

返回0至9之间的随机数

StrToInt(copy(FloatToStr(Random),4,1))

注意:在使用Random之前,请检查FloatToStr(Random)的长度,或者从小数部分使用任何其他数字。

1
生成0到9范围内的随机整数的推荐调用是“random(10)”。使用floattostr很聪明,但是给定的表达式有时会失败。考虑以下情况:“randseed:= -1498392781; X:= StrToInt(copy(FloatToStr(Random),4,1));”在这种情况下,random将返回确切的0.5,floattostr将返回“0.5”,因为没有第四个字符,copy将返回空字符串,并且StrToInt将失败并引发异常。 - David Dubois
这个想法是用更大的离散度来举例(请参见链接)。因此,您可以根据自己的需求自由地增强此算法... - DejanR

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