Terapkan PCG

31

Apa masalah yang lebih baik untuk PCG.SE daripada mengimplementasikan PCG, A Better Random Number Generator ? Makalah baru ini mengklaim menghadirkan generator angka acak cepat, sulit diprediksi, kecil, optimal secara statistik.

Penerapan C minimalnya hanya sekitar sembilan baris:

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

(dari: http://www.pcg-random.org/download.html )

Pertanyaannya adalah: dapatkah Anda berbuat lebih baik?

Aturan

Tulis program atau tentukan fungsi yang mengimplementasikan PCG pada bilangan bulat 32-bit yang tidak ditandatangani. Ini cukup luas: Anda dapat mencetak urutan yang tidak terbatas, menentukan pcg32_random_rfungsi dan struct yang sesuai, dll.

Anda harus dapat menyemai generator nomor acak Anda dengan setara dengan fungsi C berikut:

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

(dari pcg_basic.c:37:)

Memanggil generator nomor acak tanpa menaburinya terlebih dahulu adalah perilaku yang tidak terdefinisi.

Untuk dengan mudah memeriksa kiriman Anda, verifikasi bahwa, ketika diunggulkan dengan initstate = 42dan initseq = 52, hasilnya adalah 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

Mencetak gol

Penilaian standar. Diukur dalam byte. Terendah adalah yang terbaik. Dalam hal seri, pengajuan sebelumnya menang. Celah standar berlaku.

Solusi sampel

Kompilasi dengan gcc -W -Wallrapi (versi 4.8.2).

Bandingkan kiriman Anda dengan ini untuk memastikan Anda mendapatkan urutan yang sama.

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

Urutan:

2380307335
948027835
187788573
3952545547
2315139320
3279422313
2401519167
2248674523
3148099331
3383824018
2720691756
2668542805
2457340157
3945009091
1614694131
4292140870
wchargin
sumber
Jadi, apakah bahasa tugas Anda terkait?
Knerd
@Knerd Nggak. C hanyalah sebuah contoh.
wchargin
Tidak sabar untuk melihat implementasi javascript kecil ..
Daniel Baird

Jawaban:

17

CJam, 109 107 104 98 91 byte

Ini menggunakan beberapa karakter di luar rentang ASCII, tetapi mereka semua dalam ASCII yang diperluas, jadi saya menghitung setiap karakter sebagai byte (alih-alih menghitung sebagai UTF-8).

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

Ini pada dasarnya adalah port yang tepat dari kode C.

Fungsi seed adalah blok yang disimpan S, dan fungsi acak adalah blok yang disimpan R. Smengharapkan initstatedan initseqpada stack dan seed the PRNG. Rtidak mengharapkan apa pun di tumpukan dan meninggalkan nomor acak berikutnya.

Karena menelepon Rsebelum memanggil Sadalah perilaku yang tidak terdefinisi, saya sebenarnya mendefinisikan di R dalam S , jadi sampai Anda menggunakan blok seed, Rhanyalah string kosong dan tidak berguna.

The statedisimpan dalam variabel Adan incdisimpan dalam B.

Penjelasan:

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

Dan di sini adalah setara dengan test harness di OP:

42 52 S
{RN}16*

yang mencetak angka yang sama persis.

Uji di sini. Stack Exchange menghapus dua karakter yang tidak patut, sehingga tidak akan berfungsi jika Anda menyalin cuplikan di atas. Salin kode dari penghitung karakter sebagai gantinya.

Martin Ender
sumber
Dikonfirmasi: berfungsi seperti yang diiklankan.
wchargin
11

C, 195

Saya pikir seseorang harus memposting implementasi C yang lebih kompak, bahkan jika tidak ada peluang untuk menang. Baris ketiga berisi dua fungsi: r()(setara dengan pcg32_random_r()) dan s()(setara dengan pcg32_srandom_r()). Baris terakhir adalah main()fungsi, yang dikecualikan dari jumlah karakter.

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

Meskipun kompiler akan mengeluh, ini harus bekerja dengan baik pada mesin 64-bit. Pada mesin 32-bit Anda harus menambahkan lain 5 bytes untuk perubahan #define L longke #define L long long( seperti di ideone demo ini ).

osifrque melengking
sumber
Dikonfirmasi: berfungsi seperti yang diiklankan untuk saya (GCC 4.8.2 pada Mint 64-bit).
wchargin
Saya harus memutuskan bahwa srandomfungsi tersebut adalah bagian dari kiriman Anda dan harus dimasukkan dalam jumlah karakter. (Lagi pula, mungkin Anda bisa memikirkan cara cerdas untuk mengoptimalkan ini.) Ini akan membuat skor Anda saat ini hingga 197, menurut hitungan saya.
wchargin
@WChargin Ah, baiklah kalau begitu. Saya menghitung 195 byte.
ossifrage melengking
5

Julia, 218 199 191 byte

Mengganti nama tipe data ditambah beberapa trik kecil lainnya membantu saya mengurangi panjangnya dengan tambahan 19 byte. Terutama dengan menghilangkan dua :: penugasan tipe 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))

Penjelasan nama-nama (dengan nama yang sesuai dalam versi yang tidak disunat di bawah):

# 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

Inisialisasi seed dengan state 42 dan sequence 52. Karena program yang lebih kecil Anda perlu menyatakan tipe data secara eksplisit selama inisialisasi sekarang (disimpan 14 byte kode atau lebih). Anda dapat menghilangkan penetapan tipe eksplisit pada sistem 64-bit:

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

Menghasilkan set angka acak pertama:

b(r)

Hasil:

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

Saya benar-benar terkejut, bahwa bahkan versi Julia saya yang ungolfed di bawah ini jauh lebih kecil (543 byte) daripada solusi sampel dalam C (958 byte).

Versi tidak dikoleksi, 543 byte

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

Anda menginisialisasi benih (keadaan awal = 42, urutan awal = 52) dengan:

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

Kemudian Anda dapat membuat angka acak dengan:

pcg32randomr(rng)

Ini adalah hasil dari skrip uji:

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

Karena saya seorang programmer yang tidak karuan, saya butuh waktu hampir sehari untuk membuatnya bekerja. Terakhir kali saya mencoba coding sesuatu dalam C (sebenarnya C ++) hampir 18 tahun yang lalu, tetapi banyak google-fu akhirnya membantu saya untuk membuat versi Julia yang berfungsi. Saya harus mengatakan, saya telah belajar banyak hanya dalam beberapa hari tentang codegolf. Sekarang saya bisa mulai bekerja pada versi Piet. Itu akan menjadi banyak pekerjaan, tapi saya benar-benar ingin generator angka acak (bagus) untuk Piet;)

ML
sumber