Bir PCG uygulayın


31

PCG.SE için , Better Rastgele Sayı Üreticisi olan PCG'yi uygulamaktan daha iyi ne olabilir ? Bu yeni makale , hızlı, tahmin edilmesi zor, küçük, istatistiksel olarak en uygun rasgele sayı üreteci olduğunu iddia ediyor.

Minimum C uygulaması sadece dokuz satırdan ibarettir:

// *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 adresinden )

Soru şu: daha iyisini yapabilir misin?

kurallar

Bir program yazın veya PCG'yi 32 bit işaretsiz tam sayılara uygulayan bir işlev tanımlayın. Bu oldukça geniştir: sonsuz bir dizi yazdırabilir, bir pcg32_random_rişlev tanımlayabilir ve karşılık gelen yapıyı vb.

Rasgele sayı üretecinizi aşağıdaki C işlevine eşit olarak ekebilmeniz gerekir:

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

(from pcg_basic.c:37:)

Rasgele sayı üretecini ilk tohumlamadan çağırmak tanımsız davranıştır.

Gönderinizi kolayca kontrol etmek için, seed ile ekildiğinde initstate = 42ve initseq = 52çıktının aşağıdaki gibi olduğunu doğrulayın 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

puanlama

Standart puanlama Bayt cinsinden ölçülür. Düşük en iyisidir. Beraberlik durumunda, daha önce yapılan sunum kazanır. Standart boşluklar uygulanır.

Örnek çözüm

Temizce gcc -W -Wallderlenir (versiyon 4.8.2).

Aynı sırayı aldığınızdan emin olmak için gönderiminizi bununla karşılaştırın.

#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;
}

Sıra:

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

Peki görev diliniz ilgili mı?
Knerd

@Knerd Hayır. C sadece bir örnek.
wchargin

Küçük bir javascript uygulaması görmek için sabırsızlanıyorum ..
Daniel Baird

Yanıtlar:


17

CJam, 109 107 104 98 91 bayt

Bu, ASCII aralığının dışındaki bazı karakterleri kullanır, ancak hepsi genişletilmiş ASCII içindedir, bu nedenle her karakteri bayt olarak sayıyorum (UTF-8 olarak saymak yerine).

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

Bu temelde C kodunun tam bir limanıdır.

Tohum işlevi saklanan bir bloktur Sve rastgele işlev saklanan bir bloktur R. Sbekliyor initstateve initseqistifin üzerinde ve PRNG'yi tohumla. Ryığında hiçbir şey beklemiyor ve bir sonraki rasgele sayıyı bunun üzerine bırakıyor.

Çağrmadan Rönce çağırmanın Stanımsız davranışı olduğu için aslında R içinde tanımlıyorum S, yani tohum bloğunu kullanana kadar Rsadece boş bir dize ve yararsızdır.

stateDeğişken olarak depolanır Ave incsaklanır B.

Açıklama:

"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).";

Ve işte OP'deki test kablo demetinin eşdeğeri:

42 52 S
{RN}16*

Bu aynı numaraları basar.

Burada test et. Yığın Değişimi, yazdırılamayan iki karakteri soyar, bu nedenle yukarıdaki snippet'i kopyalarsanız çalışmaz. Kodu , karakter sayacından yerine kopyalayın .


Onaylandı: reklamı yapıldığı gibi çalışır.
wchargin

11

C, 195

Kazanma şansı kalmasa da, birinin daha kompakt bir C uygulaması yayınlaması gerektiğini düşündüm. Üçüncü satır iki işlev içerir: r()(eşdeğeri pcg32_random_r()) ve s()(eşdeğeri pcg32_srandom_r()). Son satır, main()karakter sayısından hariç tutulan fonksiyondur.

#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));}

Derleyici şikayet edecek olsa da, bu 64-bit bir makinede düzgün çalışması gerekir. 32 bit bir makinede, değiştirmek #define L longiçin 5 bayt daha eklemeniz gerekir #define L long long( bu ideone demosundaki gibi ).


Onaylandı: benim için ilan edildiği gibi çalışıyor (Mint 64-bit için GCC 4.8.2).
wchargin

Bunu kural olurdu srandomişlevi gönderiminize bir parçasıdır ve karakter sayısına dahil edilmelidir. (Sonuçta, belki de bunu optimize etmenin akıllıca bir yolunu düşünebilirsiniz.) Bu, mevcut puanınızı benim sayıma göre 197'ye çıkarır.
wchargin

@WChargin Ah, tamam o zaman. 195 bayt saydım.
pembemsi ossifrage,

5

Julia 218 199 191 bayt

Veri türlerini ve diğer birkaç küçük hileyi yeniden adlandırma, uzunluğu 19 bayt daha azaltmamda yardımcı oldu. Esas olarak iki :: Int64 tipi ödevi atlayarak .

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))

İsimlerin açıklaması (aşağıdaki asılsız versiyondaki isimlere göre):

# 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

Tohum durumu 42 ve dizi 52 ile sıfırlayın. Daha küçük program nedeniyle, şimdi başlatma sırasında veri tipini açıkça belirtmeniz gerekir (14 bayt kod veya benzeri). 64-bit sistemlerde açık tür atamasını atlayabilirsiniz:

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

İlk rastgele sayı kümesini üret:

b(r)

Sonuç:

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
             .
             .
             .

Gerçekten şaşırmıştım, aşağıdaki unungolfed Julia versiyonum bile C'deki (958 bayt) örnek çözeltiden çok daha küçük (543 bayt).

Ungolfed versiyonu, 543 bayt

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

Tohum eki (ilk durum = 42, ilk sıra = 52) ile başlatırsınız:

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

Ardından ile rasgele sayılar oluşturabilirsiniz:

pcg32randomr(rng)

İşte bir test betiğinin sonucu:

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

Ben berbat bir programcı olduğum için, çalışmasını sağlamak neredeyse bir günümü aldı. En son C'ye bir şeyi kodlamayı denediğimde (aslında C ++) neredeyse 18 yıl önceydi, ancak bir çok google-fu nihayet çalışan bir Julia sürümü oluşturmama yardımcı oldu. Söylemeliyim ki, kodgolf ile ilgili birkaç gün boyunca çok şey öğrendim. Şimdi bir Piet sürümü üzerinde çalışmaya başlayabilirim. Bu çok fazla iş olacak, ama gerçekten, gerçekten , Piet için (iyi) rasgele bir sayı üreteci istiyorum;)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.