构建一个通过Diehard测试的随机数生成器


50

尽管这里有许多涉及随机性的代码高尔夫问题,但我还没有看到有人真正要求构建算法伪随机数生成器。有一个要求您生成一个比特流,但是在那个端口上提供的随机性测试不是很严格,也不是代码高尔夫。

您编写的程序将具有一个可调用的函数,该函数将返回一个从0到4294967295的随机整数。此函数不得调用未作为程序一部分编写的任何库或其他函数,尤其是对/ dev / random的调用或语言的内置rand()库。更具体地说,您限于所用语言的基本运算符,例如算术,数组访问和条件流控制语句。

您的程序的分数计算如下:

Score = C / R

其中C是代码的长度(以字符为单位),R是生成器通过的Diehard测试次数(如果您的随机数生成器未通过至少一项Diehard测试,则其得分为无穷大且不合格)。如果生成器生成的文件提供的P值范围似乎沿着间隔[0,1)均匀分布,则您的生成器通过了Diehard测试。

要计算R,请使用随机数生成器及其默认种子来生成16 MB的二进制数据文件。该函数的每次调用都返回四个字节。如果您的函数太慢而无法返回字节,则这将导致折衷考虑在测试难度上获得较低的分数。然后,通过Diehard测试运行它并检查提供的P值。(不要尝试自己实现这些;请使用此处提供的内容)

当然,最低分获胜。


是否允许需要互联网连接的代码?(不能在线访问任何随机函数,但可以ping或api调用的值)
elssar 2013年

“此函数不得调用未作为程序一部分编写的任何库或其他函数。” 这包括Internet连接功能。您这一代人应该纯粹是算法上的。
Joe Z.

diehard套件期望输入文件为10-11 MB。
2013年

与测试的链接似乎已断开,这是一种可能的选择。
2012rcampion

对于我的断然答案(该内容已在下面删除)应该怎么做?我认为代码太慢而无法实用
Christopher

Answers:


6

Mathematica,32/15 = 2.133

x=3;Mod[x=Mod[x^2,28!-67],2^32]&

BBS的直接实现。

生成的二进制文件:

f = %; (* assigns anonymous function declared in the previous expression to f *)
Export["random.bin", Array[f, 2^22], "UnsignedInteger32"];

结果摘要:

 1. BIRTHDAY SPACINGS TEST           .684805
 2. OVERLAPPING 5-PERMUTATION TEST   .757608/.455899
 3. BINARY RANK TEST                 .369264/.634256
 4. BINARY RANK TEST                 .838396
 5. THE BITSTREAM TEST                (no summary p-value)    
 6. OPSO, OQSO and DNA                (no summary p-value)
 7. COUNT-THE-1's TEST               .649382/.831761
 8. COUNT-THE-1's TEST                (no summary p-value)
 9. PARKING LOT TEST                 .266079
10. MINIMUM DISTANCE TEST            .493300
11. 3DSPHERES TEST                   .492809
12. SQEEZE                           .701241
13. OVERLAPPING SUMS test            .274531
14. RUNS test                        .074944/.396186/.825835/.742302
15. CRAPS TEST                       .403090/.403088/.277389

random.bin这里满。

完整的日志文件在这里。


28!-67有点禁止。是否有一个较小的值适合64位整数?
2016年

@primo像Python一样,Mathematica的整数默认情况下是任意精度的,因此不会造成问题。
2012rcampion

我是为移植到C而专门考虑的
。– primo


21

Perl 28/13≈2.15

sub r{$s^=~($s^=$s/7215)<<8}

日志文件在这里

Perl 29/13≈2.23

sub r{$s^=~($s^=$s<<8)/60757}

日志文件在这里

这些是Xorshift的变体,使用浮点除法而不是右移。他们都通过了15项测试中的13项,仅通过了6项和7项测试。

我不确定周期有多长,但是由于以下代码不会在任何短时间内终止,因此可能是完整的2 32

$start = r();
$i++ while $start != r();
print $i;

Perl 39/10 = 3.9

$s=$^T;sub r{~($s=$s*$s%4294969373)||r}

注意:如果您正在寻找Blum-Blum-Shub风格的PRNG,Keith Randall的解决方案将比这两种方案都要好。

与下面的原始解决方案一样,这也是Blum Blum Shub的实现,但有一个主要区别。我使用的模数略大于2 32M = 50971•84263),并且每当遇到一个值,它不是有效的32位整数(即大于2 32)时,它将返回旋转。从本质上讲,这些值被删减,其余的旋转不受干扰,从而导致几乎均匀的分布。

似乎有所帮助。除了通过与以前相同的9个测试之外,它现在还令人信服地通过了最小距离测试。可以在此处找到示例日志文件。


Perl 33/9≈3.67(无效?)

 $s=$^T;sub r{$s=$s*$s%4294951589}

注意:此解决方案可能被认为是无效的,因为永远不会观察到该范围的最高0.00037%。

Blum Blum Shub的快速而肮脏的实现。我声明以下结果:

 1. passed - Birthday Spacings
 2. FAILED - Overlapping Permutations
 3. passed - Ranks of 31x31 and 32x32 Matrices
 4. passed - Ranks of 6x8 Matrices
 5. FAILED - Monkey Tests on 20-bit Words
 6. FAILED - Monkey Tests OPSO, OQSO, DNA
 7. FAILED - Count the 1s in a Stream of Bytes
 8. passed - Count the 1s for Specific Bytes
 9. passed - Parking Lot Test
10. FAILED - Minimum Distance Test
11. passed - Random Spheres Test
12. FAILED - The Squeeze Test
13. passed - Overlapping Sums Test
14. passed - Runs Test
15. passed - The Craps Test

您可以在此处找到示例日志文件,可以随意质疑任何结果。diehard的文件可以通过以下方式生成:

print pack('N', r()) for 1..4194304

然后将输出传递到文件中。最小距离看起来可能已经过去了,但是如果您多次运行它,它总是非常接近1.0,这表明失败了。


细节

通常,Blum Blum Shub是一种糟糕的PRNG,但可以通过选择良好的模量来提高其性能。我选择的M7027•611207。这两个素因pq都具有模余数3(mod 4),并且gcd(φ(p-1),φ(q-1))= 2尽可能小。

尽管这些是Wiki页面上列出的唯一条件,但这似乎还不够。我尝试的所有模数几乎都在每次测试中均失败。但是只有少数几个可以通过一些测试,无论出于何种原因,我选择的测试似乎都非常出色。

最后一点,测试5本身似乎可以很好地表明PRNG的良好程度。如果它几乎没有通过测试5,它将使其余的测试失败。


奖金:Perl 62/14≈4.43

$t=$^T;sub r{$t|=(($s=$s/2|$t%2<<31)^($t/=2))<<31for 1..37;$t}

仅出于怪味,这是原始Tetris for NES中使用的PRNG的32位版本。令人惊讶的是,它通过了15个测试中的14个!

 1. passed - Birthday Spacings
 2. passed - Overlapping Permutations
 3. passed - Ranks of 31x31 and 32x32 Matrices
 4. passed - Ranks for 6x8 Matrices
 5. passed - Monkey Tests on 20-bit Words
 6. passed - Monkey Tests OPSO, OQSO, DNA
 7. FAILED - Count the 1s in a Stream of Bytes
 8. passed - Count the 1s for Specific Bytes
 9. passed - Parking Lot Test
10. passed - Minimum Distance Test
11. passed - Random Spheres Test
12. passed - The Squeeze Test
13. passed - Overlapping Sums Test
14. passed - Runs Test
15. passed - The Craps Test

示例日志文件可以在此处

诚然,这1..37不是一个精确的抄写。在原始版本中,熵例程每秒更新60次,然后以随机间隔查询,这在很大程度上取决于用户输入。对于需要拆解ROM的任何人,熵例程均始于0xAB47

Python样式的伪代码:

carry = entropy_1 & 1
entropy_1 >>= 1
entropy_2 = (entropy_2 >> 1) | (carry << 31)
carry = (entropy_1 & 1) ^ (entropy_2 & 1)
entropy_1 |= carry << 31

是的,我注意到您的算法“未通过”比特流测试,但实际上有一些低于0.999999的值。不过,您的测试似乎准确无误。
Joe Z.

但是,存在一个问题,那就是从4294951589到4294967295的数字几乎没有发生的可能性(尽管我认为这是它在某些Diehard测试中失败的原因的一部分)。
Joe Z.

1
@JoeZeng是的,这是一个问题。在测试5中最明显:第一轮遗漏了151k个单词,而其余仅遗漏了143k个单词。一种解决方案是选择一个略大于2 ^ 32的模数,并允许太大的值绕回零,但是我找不到能很好工作的模数。如果这样做,我将更新帖子。
2013年

7

蟒蛇,46/15 = 3.0666

v=3
def R():global v;v=v**3%(2**32-5);return v

使用模幂运算产生随机性。2 ** 32-5是小于2 ^ 32的最大素数。(与无法运行测试2相同。)


您可以粘贴日志文件吗?
2013年


1
愚蠢的Windows。它会将\r和的所有出现都转换\n\r\n,这显然会使结果产生偏差。解决方法是直接使用f = open('file.bin', 'wb')和写入文件f.write
2013年

这个新分数会降低先前的分数,因此现在是已接受的答案。
Joe Z.

新分数再次被削弱,因此我更改了已接受的答案。
Joe Z.

4

Ruby,32/15 = 2.1333

这是Keith Randall的解决方案,以Ruby实现。

$v=3;def R;$v=$v**3%(2**32-5)end

@JoeZ这似乎是新的最低答案,与全新的Mathematica答案相关。
2013年

3

C#144/15 = 9.6

uint a=15,b=26,y;uint q(int n){y=(a*1414549U+876619U)^(b*889453U+344753U);b=a;a=y>>12;return(a%256)<<n;}uint r(){return q(24)|q(16)|q(8)|q(0);}

通过了所有测试。

由于没有太多的字符,它通过了TestU01。

结果:http : //codepad.org/iny6usjV

    uint a = 15;
    uint b = 26;

    byte prng8()
    {
        uint y = ((a * 1414549U + 876619U) ^ (b * 889453U + 344753U)) >> 12;
        b = a;
        a = y;
        return (byte)y;
    }

    uint prng32()
    {
        return ((uint)prng8() << 24) | ((uint)prng8() << 16) | ((uint)prng8() << 8) | (uint)prng8();
    }

2

C#-103/14 = 7.36

double j=999;uint N(){uint i=0,n=0;for(;i++<4;n=n*256+(uint)j%256)for(j/=277;j<100000;j*=j);return n;}

结果

通过所有测试6以外的内容,
请参见http://codepad.org/k1NSoyQW上的结果

说明

像往常一样,C#不能与Ruby和Python竞争简洁性,但是我很喜欢尝试。当然,还有其他一些值也会起作用(例如,j = 999的初始值,除数= 277的初始值)。经过简短的实验,我选择了这些。

使用文件创建包装器

class R
{
    public static void Main(string[] args)
    {
        var r = new R();
        using (var f = new System.IO.FileStream(".\\out.bin", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.Read))
        using (var b = new System.IO.BinaryWriter(f))
        {
            for (long i = 0; i < 12 * 1024 * 1024; i += 4)
            {

                b.Write(r.N());
            }
        }
    }

    double j = 999;

    uint N()
    {
        uint i = 0, n = 0;
        for (; i++ < 4; n = n * 256 + (uint)j % 256)
            for (j /= 277; j < 100000; j *= j) ;
        return n;
    }

}

1

蟒蛇,41/15 = 2.73333

v=0
def R():global v;v=hash(`v`);return v

Kinda使用内置的哈希函数作弊,但它内置的,因此不比使用其他内置函数作弊更多len。另一方面,我不得不为该global v;声明付费。

通过所有Diehard测试(我在测试2上遇到问题,它在OSX机器上是SEGV。就我的分数而言,我假设它会通过)。

这是生成16MB文件的驱动程序:

import sys
for i in xrange(1<<22):
  r=R()
  sys.stdout.write('%c%c%c%c'%(r&255, r>>8&255, r>>16&255, r>>24&255))

“此函数不得调用未作为程序一部分编写的任何库或其他函数,尤其是对/ dev / random或语言的内置rand()库的调用。” 抱歉,您的参赛资格被取消。
Joe Z.

要明确的是,“ len”也将取消您的参赛资格。
Joe Z.

您在哪里划界线?是+内置函数,因此被取消资格吗?
基思·兰德尔

6
但是在许多语言中,运算符和功能都是相同的。参见+__add__在python中,或在c ++中进行运算符重载。我知道我有点发hair,所以考虑这个例子。我可以在python中创建这样的地图{'a':5}吗?您可能会说“是”,但请考虑一下,hash('a')这样做时会被调用。
基思·兰德尔

2
我想当您需要以这种方式在语法上引用该函数时,我会划清界线。如果您可以在Python中找到一种可让您直接访问映射地址而无需在语法上引用“哈希”函数的黑客,那么我可能会接受。
Joe Z.

1

C,38/15 = 2.533

long long x;f(){return(x+=x*x+9)>>32;}

我无法在我的机器上进行Diehard测试,但是它通过了PractRand套件,可提供高达8GB的输出,因此我认为它将全部通过。


0

Brain-Flak,344 /(待审)

<>((()()){})<> push the amount of iterations to do for the PRNG
(((((((((((((((((((((((((((((((((((()()()){}()){})){}{}){()()()()({}[()])}{})){}{})){}{})()){}{})()){}{})){}{})){}{}){}())){}{})){}{})()){}{})()){}{})){}{})){}{})()){}{})()){}{}) push M (one of the values for the Blum Blum Shub PRNG
((((((((((((()()()){}){}){})){}{}){()({}[()])}{}){}())){}{})()){}{}) push s see above
<>{({}[()])<>starts the loop
(({({})({}[()])}{}) squares the current number
(<>))<>{(({})){({}[()])<>}{}}{}<>([{}()]({}))mods by M
<>}{}<>loop ends

在线尝试!

这很好用,但是顽固的测试链接都坏了:(所以直到我们得到新的链接,我才没有最终成绩

这使用了Blum Blum Shub PRNG,因此应该可以通过大多数情况。使用的数字足够大,在16 MB的测试用例中不会出现任何模式


如果这无效,请告诉我
Christopher

1
我数344。定理:没有完全打高尔夫球的Brain-flak程序的字节数为奇数。
user202729 '17

0

物镜-C,40/1 = 40

很聪明的方法,.hash在这里利用了一些作弊手段,但是我喜欢

for(int v=9;v=@(v).hash;printf("%i",v));
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.