PCGを実装する


31

PCG。A Better Random Number Generatorを実装するよりもPCG.SEの方が良い問題はありますか?この新しい論文は、高速で予測が難しく、小さく、統計的に最適な乱数ジェネレーターを提供すると主張しています。

最小限のC実装は約9行です。

// *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));
}

(from: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

あなたのタスク言語は関連していますか?
ケナード14

@Knerdいや。Cは単なる例です。
wchargin

小さなJavaScriptの実装を参照するのを待つことができない...
ダニエルベアード

回答:


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であり、ランダム関数はに格納されたブロックRです。S期待initstateしてinitseqスタックとシードPRNGの。Rスタックには何も期待せず、次の乱数を残します。

呼び出すR前に呼び出すことSは未定義の動作なので、私は実際にR withinを 定義しているSので、シードブロックを使用するまでRは空の文字列であり、無駄です。

state変数に格納されているAincに格納されます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は2つの印刷できない文字を削除するため、上記のスニペットをコピーすると機能しません。代わりに、文字カウンターからコードをコピーしてください。


確認済み:広告どおりに機能します。
wchargin

11

C、195

たとえ勝つ可能性がないとしても、誰かがよりコンパクトなC実装を投稿すべきだと考えました。第三のラインは、2つの機能が含まれている: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ビットマシンでは、変更#define L longするためにさらに5バイトを追加する必要があります#define L long longこのideoneデモのように)。


確認済み:宣伝どおりに動作します(Mint 64ビットのGCC 4.8.2)。
wchargin

このsrandom関数は送信の一部であり、文字数に含める必要があると判断する必要があります。(結局、おそらくこれを最適化するための賢い方法を考えることができるでしょう。)これはあなたの現在のスコアを私のカウントで197まで上げます。
wchargin

@WCharginああ、OK。195バイトを数えました。
squeamish ossifrage 14

5

ジュリア、 218 199 191バイト

データ型の名前の変更に加えて、いくつかの小さなトリックを追加することで、長さをさらに19バイト削減できました。主に、2つの:: 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
             .
             .
             .

私が本当に驚いたのは、以下の私の無料のジュリアバージョンでさえ、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

私はひどいプログラマーなので、それを機能させるのにほぼ1日かかりました。最後にC(実際にはC ++)で何かをコーディングしようとしたのはほぼ18年前でしたが、多くのgoogle-fuがついに実用的なJuliaバージョンを作成するのに役立ちました。私は言わなければならない、私はcodegolfでほんの数日間で多くを学んだ。これで、Pietバージョンで作業を開始できます。それは非常に多くの作業になるでしょうが、私は本当に、 Piet用の(良い)乱数ジェネレータが本当に欲しいです;)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.