Впровадити ПХГ


31

Яка краща проблема для PCG.SE, ніж впровадження PCG, кращого генератора випадкових чисел ? Цей новий документ вимагає представити швидкий, важко передбачуваний, невеликий статистично оптимальний генератор випадкових чисел.

Його мінімальна реалізація становить приблизно дев'ять рядків:

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

Питання: чи можна зробити краще?

Правила

Напишіть програму або визначте функцію, яка реалізує PCG на 32-бітних непідписаних цілих числах. Це досить широко: ви могли роздрукувати нескінченну послідовність, визначити 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 Nope. С - лише приклад.
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;

Це в основному точний порт коду С.

Насіннєва функція - це блок, що зберігається в S, а випадкова функція - це блок, що зберігається в R. Sочікує initstateі initseqна стек і насінь 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).";

І ось еквівалент тестового джгута в ОП:

42 52 S
{RN}16*

який друкує абсолютно однакові цифри.

Тестуйте це тут. Stack Exchange знімає два недрукованих символи, тому він не працюватиме, якщо скопіювати наведений вище фрагмент. Скопіюйте код замість лічильника символів .


Підтверджено: працює як рекламується.
wchargin

11

С, 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 демо ).


Підтверджено: працює як рекламується для мене (GCC 4.8.2 на монетній 64-бітній монеті).
wchargin

Я повинен був би srandomвирішити, що функція є частиною вашої подачі і повинна бути включена до числа символів. (Зрештою, можливо, ви могли б придумати якийсь розумний спосіб оптимізувати це.) Це, на мій підрахунок, призведе до вашої поточної оцінки до 197.
wchargin

@WChargin Ага, гаразд тоді. Я нарахував 195 байт.
пискливе костіння

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

Я був дуже здивований, що навіть моя версія не золотого Юлія нижче настільки менша (543 байти), ніж зразок розчину в С (958 байт).

Безгольова версія, 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;)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.