实施PCG


31

与实现更好的随机数生成器PCG相比,PCG.SE有什么更好的问题?这份新论文声称将提供一种快速,难以预测的小型统计上最优的随机数生成器。

它的最小C实现仅约九行:

// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)

typedef struct { uint64_t state;  uint64_t inc; } pcg32_random_t;

uint32_t pcg32_random_r(pcg32_random_t* rng)
{
    uint64_t oldstate = rng->state;
    // Advance internal state
    rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1);
    // Calculate output function (XSH RR), uses old state for max ILP
    uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
    uint32_t rot = oldstate >> 59u;
    return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}

(摘自:http : //www.pcg-random.org/download.html

问题是:你能做得更好吗?

规则

编写程序或定义在32位无符号整数上实现PCG的函数。这是相当广泛的:您可以打印出无限序列,定义pcg32_random_r函数和对应的结构等。

您必须能够等效于以下C函数为随机数生成器提供种子:

// pcg32_srandom(initstate, initseq)
// pcg32_srandom_r(rng, initstate, initseq):
//     Seed the rng.  Specified in two parts, state initializer and a
//     sequence selection constant (a.k.a. stream id)

void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
{
    rng->state = 0U;
    rng->inc = (initseq << 1u) | 1u;
    pcg32_random_r(rng);
    rng->state += initstate;
    pcg32_random_r(rng);
}

(来自:pcg_basic.c:37

调用随机数生成器而不先播种它是未定义的行为。

为了轻松检查您的提交,请在将initstate = 42和用作种子时,确认initseq = 52输出为2380307335

$ tail -n 8 pcg.c 
int main()
{
    pcg32_random_t pcg;
    pcg32_srandom_r(&pcg, 42u, 52u);

    printf("%u\n", pcg32_random_r(&pcg));
    return 0;
}
$ gcc pcg.c
$ ./a.out 
2380307335

计分

标准评分。以字节为单位。最低是最好。如果并列,则较早提交。 有标准漏洞

样品溶液

gcc -W -Wall干净地编译(版本4.8.2)。

比较您的提交与此,以确保您获得相同的顺序。

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>

typedef struct { uint64_t state;  uint64_t inc; } pcg32_random_t;

uint32_t pcg32_random_r(pcg32_random_t* rng)
{
    uint64_t oldstate = rng->state;
    // Advance internal state
    rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1);
    // Calculate output function (XSH RR), uses old state for max ILP
    uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
    uint32_t rot = oldstate >> 59u;
    return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}

void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
{
    rng->state = 0U;
    rng->inc = (initseq << 1u) | 1u;
    pcg32_random_r(rng);
    rng->state += initstate;
    pcg32_random_r(rng);
}

int main()
{
    size_t i;
    pcg32_random_t pcg;
    pcg32_srandom_r(&pcg, 42u, 52u);

    for (i = 0; i < 16; i++)
    {
        printf("%u\n", pcg32_random_r(&pcg));
    }
    return 0;
}

序列:

2380307335
948027835
187788573
3952545547
2315139320
3279422313
2401519167
2248674523
3148099331
3383824018
2720691756
2668542805
2457340157
3945009091
1614694131
4292140870

那么您的任务语言是否相关?
Knerd 2014年

@Knerd Nope。C只是一个例子。
wchargin 2014年

等不及要看到一个小的JavaScript实现..
丹尼尔·贝尔德

Answers:


17

CJam,109个 107 104 98 91字节

这使用了ASCII范围之外的一些字符,但是它们都在扩展的ASCII范围内,因此我将每个字符作为一个字节计数(而不是作为UTF-8计数)。

{[2*0:A)|:B{AA"XQô-L-"256b*B1|+GG#%:A;__Im>^27m>_@59m>_@\m>@@~)31&m<|4G#%}:R~@A+:AR];}:S;

这基本上是C代码的确切端口。

种子函数是存储在中的块S,随机函数是存储在中的块RS期望initstateinitseq在堆栈上播种PRNG。R不期望堆栈上有任何东西,并在其上留下下一个随机数。

由于在调用R之前进行调用S是未定义的行为,因此我实际上是R 在内 定义S,因此在您使用种子块之前,R它只是一个空字符串且无用。

state储存在变量中A,将inc储存在中B

说明:

"The seed block S:";
[       "Remember start of an array. This is to clear the stack at the end.";
2*      "Multiply initseq by two, which is like a left-shift by one bit.";
0:A     "Store a 0 in A.";
)|:B    "Increment to get 1, bitwise or, store in B.";
{...}:R "Store this block in R. This is the random function.";
~       "Evaluate the block.";
@A+:A   "Pull up initstate, add to A and store in A.";
R       "Evaluate R again.";
];      "Wrap everything since [ in an array and discard it.";

"The random block R:";
AA            "Push two A's, one of them to remember oldstate.";
"XQô-L-"256b* "Push that string and interpret the character codes as base-256 digits.
               Then multiply A by this.";
B1|+          "Take bitwise or of 1 and inc, add to previous result.";
GG#%:A;       "Take modulo 16^16 (== 2^64). Store in A. Pop.";
__            "Make two copies of old state.";
Im>^          "Right-shift by 18, bitwise xor.";
27m>_         "Right-shift by 27. Duplicate.";
@59m>         "Pull up remaining oldstate. Right-shift by 59.";
_@\m>         "Duplicate, pull up xorshifted, swap order, right-shift.";
@@            "Pull up other pair of xorshifted and rot.";
~)            "Bitwise negation, increment. This is is like multiplying by -1.";
31&           "Bitwise and with 31. This is the reason I can actually use a negative value
               in the previous step.";
m<|           "Left-shift, bitwise or.";
4G#%          "Take modulo 4^16 (== 2^32).";

这等效于OP中的测试工具:

42 52 S
{RN}16*

打印完全相同的数字。

在这里测试。Stack Exchange会删除两个无法打印的字符,因此如果您复制上述代码段,它将无法正常工作。而是从字符计数器复制代码。


已确认:如广告中所述。
wchargin

11

C,195

我认为即使没有机会获胜,也应该发布更紧凑的C实现。第三行包含两个函数:(r()等同于pcg32_random_r())和s()(等同于pcg32_srandom_r())。最后一行是main()函数,该函数从字符计数中排除。

#define U unsigned
#define L long
U r(U L*g){U L o=*g;*g=o*0x5851F42D4C957F2D+(g[1]|1);U x=(o>>18^o)>>27;U t=o>>59;return x>>t|x<<(-t&31);}s(U L*g,U L i,U L q){*g++=0;*g--=q+q+1;r(g);*g+=i;r(g);}
main(){U i=16;U L g[2];s(g,42,52);for(;i;i--)printf("%u\n",r(g));}

尽管编译器会抱怨,但这在64位计算机上应该可以正常工作。在32位机器,你就必须另外5个字节添加到改变#define L long#define L long long就像这个ideone演示)。


已确认:按我的广告运作(薄荷64位元上的GCC 4.8.2)。
wchargin 2014年

我必须裁定该srandom功能是您提交的内容的一部分,并且应包括在字符计数中。(毕竟,也许您可​​以想到一些优化此方法的聪明方法。)根据我的计算,这会使您目前的成绩提高到197。
wchargin 2014年

@WChargin啊,那好。我数了195个字节。
吱吱作响的ossifrage,2014年

5

朱莉娅 218 199 191字节

重命名数据类型以及其他一些小技巧,使我减少了19个字节的长度。主要是通过省略两个:: Int64类型分配。

type R{T} s::T;c::T end
R(s,c)=new(s,c);u=uint32
a(t,q)=(r.s=0x0;r.c=2q|1;b(r);r.s+=t;b(r))
b(r)=(o=uint64(r.s);r.s=o*0x5851f42d4c957f2d+r.c;x=u((o>>>18$o)>>>27);p=u(o>>>59);x>>>p|(x<<-p&31))

名称说明(在以下非高尔夫版本中具有相应的名称):

# R     : function Rng
# a     : function pcg32srandomr
# b     : function pcg32randomr
# type R: type Rng
# r.s   : rng.state
# r.c   : rng.inc
# o     : oldstate
# x     : xorshifted
# t     : initstate
# q     : initseq
# p     : rot
# r     : rng
# u     : uint32

使用状态42和序列52初始化种子。由于程序较小,因此您现在需要在初始化期间显式声明数据类型(保存14个字节的代码左右)。您可以在64位系统上省略显式类型分配:

r=R(42,52) #on 64-bit systems or r=R(42::Int64,52::Int64) on 32 bit systems
a(r.s,r.c)

产生第一组随机数:

b(r)

结果:

julia> include("pcg32golfed.jl")
Checking sequence...
result round 1: 2380307335
target round 1: 2380307335   pass
result round 2: 948027835
target round 2: 948027835   pass
result round 3: 187788573
target round 3: 187788573   pass
             .
             .
             .

我真的很惊讶,即使我下面的未使用Julia版本也比C语言中的示例解决方案(958字节)小得多(543字节)。

非高尔夫版本,543字节

type Rng{T}
    state::T
    inc::T
end

function Rng(state,inc)
    new(state,inc)
end

function pcg32srandomr(initstate::Int64,initseq::Int64)
    rng.state =uint32(0)
    rng.inc   =(initseq<<1|1)
    pcg32randomr(rng)
    rng.state+=initstate
    pcg32randomr(rng)
end

function pcg32randomr(rng)
    oldstate  =uint64(rng.state)
    rng.state =oldstate*uint64(6364136223846793005)+rng.inc
    xorshifted=uint32(((oldstate>>>18)$oldstate)>>>27)
    rot       =uint32(oldstate>>>59)
    (xorshifted>>>rot) | (xorshifted<<((-rot)&31))
end

使用以下命令初始化种子(初始状态= 42,初始序列= 52)。

rng=Rng(42,52)
pcg32srandomr(rng.state,rng.inc)

然后,您可以使用以下方法创建随机数:

pcg32randomr(rng)

这是测试脚本的结果:

julia> include("pcg32test.jl")
Test PCG
Initialize seed...
Checking sequence...
result round 1: 2380307335
target round 1: 2380307335   pass
result round 2: 948027835
target round 2: 948027835   pass
result round 3: 187788573
target round 3: 187788573   pass
             .
             .
             .
result round 14: 3945009091
target round 14: 3945009091   pass
result round 15: 1614694131
target round 15: 1614694131   pass
result round 16: 4292140870
target round 16: 4292140870   pass

因为我是一个糟糕的程序员,所以花了我几乎一天的时间才能使它工作。上一次我尝试用C(实际上是C ++)编写代码的时间大约是18年前,但是很多google-fu最终帮助我创建了一个可正常工作的Julia版本。我不得不说,仅仅几天在Codegolf上,我学到了很多东西。现在,我可以开始处理Piet版本了。那将是很多工作,但我确实非常希望为Piet使用(良好)随机数生成器;)

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.