Побудуйте генератор випадкових чисел, який проходить тести Діярда


50

Хоча тут є багато питань щодо гольфу з кодом, що стосується випадковості, я ще не бачив жодного, який насправді просить побудувати алгоритмічний генератор псевдовипадкових чисел. Ось цей, який просить вас генерувати трохи потоку, але тести на випадковість, передбачені на цьому, були не дуже строгими, і це не код-гольф.

Програма, яку ви пишете, матиме єдину функцію дзвінка, яка повертає випадкове ціле число від 0 до 4294967295. Ця функція не повинна залучати жодних бібліотек чи інших функцій, які також не були записані як частина програми, особливо виклики в / dev / random або вбудована мовна бібліотека rand (). Більш конкретно, ви обмежуєтесь основними операторами мови, якою ви працюєте, такими як арифметичні, масиви доступу та умовні операції управління потоком.

Оцінка вашої програми розраховується так:

Score = C / R

Де C - довжина коду в символах, а R - кількість тестів Diehard, які пройшов ваш генератор (Якщо ваш генератор випадкових чисел не пройшов хоча б одного тесту Дієхара, його оцінка нескінченна, і вона дискваліфікується). Ваш генератор проходить тест Diehard, якщо створений ним файл надає діапазон P-значень, які, здається, розподіляються рівномірно по інтервалі [0, 1).

Щоб обчислити R, використовуйте свій генератор випадкових чисел із замовчуванням, щоб створити файл бінарних даних 16 Мб. Кожен виклик функції повертає чотири байти; якщо ваша функція занадто повільна для повернення байтів, це призведе до компромісу на досягнення низького бала, наскільки складно перевірити. Потім запустіть його через тести Діярда і перевірте надані значення P. (Не намагайтеся реалізувати це самостійно; використовуйте наведені тут )

Звичайно, найнижчий результат виграє.


Чи дозволений код, який вимагає підключення до Інтернету? (не збираюся отримати доступ до будь-якої випадкової функції в Інтернеті, але, можливо, пінг або значення api-дзвінка)
elssar

"Ця функція не повинна залучати до бібліотек чи інших функцій, які також не були записані як частина програми." Це включає функції підключення до Інтернету. Ваше покоління має бути суто алгоритмічним.
Джо З.

Пакет Diehard очікує вхідних файлів 10-11 Мб.
примо

Здається, посилання на тести порушена, ось можлива альтернатива.
2012р. Кемпіон

Як слід це зробити для моєї відповіді на відмову від мозку (видалено нижче)? Я думаю, що код занадто повільний, щоб бути практичним
Крістофер

Відповіді:


6

Математика, 32/15 = 2.133

x=3;Mod[x=Mod[x^2,28!-67],2^32]&

Безпосередня реалізація BBS .

Бінарний файл, створений за допомогою:

f = %; (* assigns anonymous function declared in the previous expression to f *)
Export["random.bin", Array[f, 2^22], "UnsignedInteger32"];

Підсумок результатів:

 1. BIRTHDAY SPACINGS TEST           .684805
 2. OVERLAPPING 5-PERMUTATION TEST   .757608/.455899
 3. BINARY RANK TEST                 .369264/.634256
 4. BINARY RANK TEST                 .838396
 5. THE BITSTREAM TEST                (no summary p-value)    
 6. OPSO, OQSO and DNA                (no summary p-value)
 7. COUNT-THE-1's TEST               .649382/.831761
 8. COUNT-THE-1's TEST                (no summary p-value)
 9. PARKING LOT TEST                 .266079
10. MINIMUM DISTANCE TEST            .493300
11. 3DSPHERES TEST                   .492809
12. SQEEZE                           .701241
13. OVERLAPPING SUMS test            .274531
14. RUNS test                        .074944/.396186/.825835/.742302
15. CRAPS TEST                       .403090/.403088/.277389

Повний random.binтут.

Повний файл журналу тут.


28!-67дещо заборонний. Чи є менше значення, яке б відповідало 64-бітовому цілому числу?
прим

@primo Як і Python, цілі числа Mathematica за замовчуванням є довільною точністю, тому це не викликає проблем.
2012рcampion

Я спеціально думав над переносом на C.
прим


21

Perl 28/13 ≈ 2,15

sub r{$s^=~($s^=$s/7215)<<8}

файл журналу тут

Perl 29/13 ≈ 2,23

sub r{$s^=~($s^=$s<<8)/60757}

файл журналу тут

Вони є чимось варіацією на Xorshift , використовуючи поділ з плаваючою точкою замість правого зсуву. Вони обидва проходять 13 з 15 тестів, не виконавши лише тестів 6 та 7.

Я не точно впевнений, як тривалий цикл, але оскільки наступний код не закінчується за короткий проміжок часу, швидше за все, повне 2 32 :

$start = r();
$i++ while $start != r();
print $i;

Perl 39/10 = 3,9

$s=$^T;sub r{~($s=$s*$s%4294969373)||r}

Примітка: якщо ви шукаєте PRNG Blum-Blum-Shub-esque, рішення Кіта Рандалла набагато краще, ніж будь-яке з них.

Як і моє оригінальне рішення нижче, це також реалізація Shub Blum Shub, з однією головною відмінністю. Я використовую модуль, трохи більший за 2 32 ( M = 50971 • 84263 ), і кожного разу, коли виникає значення, що воно не є дійсним 32-бітовим цілим числом (тобто більшим за 2 32 ), воно повертає наступне значення у обертання замість цього. По суті, ці значення викреслюються, залишаючи решту обертання непорушеним, що призводить до майже рівномірного розподілу.

Здається, це допомогло. Окрім того, що пройшов ті ж 9 тестів, що і раніше, він також переконливо проходить тест на мінімальну відстань. Зразок файлу журналу можна знайти тут .


Perl 33/9 ≈ 3,67 (недійсний?)

 $s=$^T;sub r{$s=$s*$s%4294951589}

Примітка. Це рішення може вважатися недійсним, оскільки найвищі 0,00037% діапазону ніколи не будуть спостерігатися.

Швидке та брудне впровадження Blum Blum Shub . Я вимагаю таких результатів:

 1. passed - Birthday Spacings
 2. FAILED - Overlapping Permutations
 3. passed - Ranks of 31x31 and 32x32 Matrices
 4. passed - Ranks of 6x8 Matrices
 5. FAILED - Monkey Tests on 20-bit Words
 6. FAILED - Monkey Tests OPSO, OQSO, DNA
 7. FAILED - Count the 1s in a Stream of Bytes
 8. passed - Count the 1s for Specific Bytes
 9. passed - Parking Lot Test
10. FAILED - Minimum Distance Test
11. passed - Random Spheres Test
12. FAILED - The Squeeze Test
13. passed - Overlapping Sums Test
14. passed - Runs Test
15. passed - The Craps Test

Зразок файлу журналу можна знайти тут , сміливо оскаржуйте будь-який результат. Файл для diehard можна створити наступним чином:

print pack('N', r()) for 1..4194304

а потім трубопровідний файл у файл. Мінімальна відстань виглядає так, що вона могла пройти, але якщо пропустити її кілька разів, це завжди дуже близько 1,0 , що вказує на збій.


Деталі

Взагалі, Blum Blum Shub - це жахливий PRNG, але його продуктивність можна покращити, вибравши хороший модуль. M Я вибрав це 7027 • 611207 . Обидва ці основні фактори, p і q , мають модульний залишок 3 (mod 4) і gcd (φ (p-1), φ (q-1)) = 2 , який є настільки низьким, наскільки це може бути.

Хоча це єдині критерії, перелічені на сторінці вікі, це здається недостатньо. Практично вся модуль, яку я спробував, провалила кожен тест. Але є жменька, яка пройде деякі тести, і те, що я вибрав, здається, надзвичайно хорошим, з будь-якої причини.

На завершення, тест 5 сам по собі здається досить хорошим показником того, наскільки хороший PRNG. Якщо він майже не пройде Тест 5, він ретельно вийде з ладу.


БОНУС: Perl 62/14 ≈ 4,43

$t=$^T;sub r{$t|=(($s=$s/2|$t%2<<31)^($t/=2))<<31for 1..37;$t}

Тільки для geekery, це 32-розрядна версія PRNG, яка використовується в оригінальному тетрісі для NES. Дивно, але він проходить 14 з 15 тестів!

 1. passed - Birthday Spacings
 2. passed - Overlapping Permutations
 3. passed - Ranks of 31x31 and 32x32 Matrices
 4. passed - Ranks for 6x8 Matrices
 5. passed - Monkey Tests on 20-bit Words
 6. passed - Monkey Tests OPSO, OQSO, DNA
 7. FAILED - Count the 1s in a Stream of Bytes
 8. passed - Count the 1s for Specific Bytes
 9. passed - Parking Lot Test
10. passed - Minimum Distance Test
11. passed - Random Spheres Test
12. passed - The Squeeze Test
13. passed - Overlapping Sums Test
14. passed - Runs Test
15. passed - The Craps Test

Зразок файлу журналу можна тут .

Справді, 1..37біт не є точною транскрипцією. У початковій версії розпорядок ентропії оновлюється 60 разів на секунду, а потім запитується через випадкові інтервали, багато в чому залежать від введення користувача. Для всіх, хто хотів би розібрати ПЗУ, розпочалася програма ентропії 0xAB47.

Псевдокод стилю Python:

carry = entropy_1 & 1
entropy_1 >>= 1
entropy_2 = (entropy_2 >> 1) | (carry << 31)
carry = (entropy_1 & 1) ^ (entropy_2 & 1)
entropy_1 |= carry << 31

Так, я помітив, що ваш алгоритм "провалив" тест бітового потоку, але насправді мав кілька значень нижче 0,999999. І все-таки ваші тести здаються точними.
Джо З.

Однак є одна проблема, і це те, що цифри від 4294951589 до 4294967295 не мають шансів виникнути (хоча, мабуть, це є причиною того, що він не вдався до деяких тестів Діярда).
Джо З.

1
@JoeZeng так, це проблема. Це найбільш очевидно в Тесті 5: у першому запуску є 151 тис. Пропущених слів, а в решті лише 143 кка відсутні. Одним з рішень було б вибрати модуль, трохи більший за 2 ^ 32, і дозволити занадто великі значення, щоб обернутися до нуля, але я не зміг знайти той, який добре працює. Якщо я це зробити, я оновлю публікацію.
прим

7

Пітона, 46/15 = 3,0666

v=3
def R():global v;v=v**3%(2**32-5);return v

Використовує модульну експоненцію для генерування випадковості. 2 ** 32-5 - це найбільший простір менше 2 ^ 32. (Це ж угода з неможливістю запустити тест №2.)


Чи можете ви вставити файл журналу?
прим

Увійдіть тут: codepad.org/ZWhoGe0t
Кіт Рандалл

1
Дурні Windows. Це конвертувало всі випадки \rта \nдо \r\n, що, очевидно, перекручує результати. Виправлення полягає в тому, щоб записати файл безпосередньо за допомогою f = open('file.bin', 'wb')і f.write.
прим

Ця нова оцінка знижує попередній бал, тому тепер прийнята відповідь.
Джо З.

Цей новий бал знову був недооцінений, тому я змінив прийняту відповідь.
Джо З.

4

Рубі, 32/15 = 2.1333

Це рішення Кіта Рандалла, реалізоване в Ruby.

$v=3;def R;$v=$v**3%(2**32-5)end

@JoeZ Це, мабуть, нова найнижча відповідь, пов'язана з абсолютно новою відповіддю Mathematica.
Riking

3

C # 144/15 = 9,6

uint a=15,b=26,y;uint q(int n){y=(a*1414549U+876619U)^(b*889453U+344753U);b=a;a=y>>12;return(a%256)<<n;}uint r(){return q(24)|q(16)|q(8)|q(0);}

Це пройшло всі випробування.

З не надто великою кількістю символів він проходить TestU01.

Результат: http://codepad.org/iny6usjV

    uint a = 15;
    uint b = 26;

    byte prng8()
    {
        uint y = ((a * 1414549U + 876619U) ^ (b * 889453U + 344753U)) >> 12;
        b = a;
        a = y;
        return (byte)y;
    }

    uint prng32()
    {
        return ((uint)prng8() << 24) | ((uint)prng8() << 16) | ((uint)prng8() << 8) | (uint)prng8();
    }

2

C # - 103/14 = 7,36

double j=999;uint N(){uint i=0,n=0;for(;i++<4;n=n*256+(uint)j%256)for(j/=277;j<100000;j*=j);return n;}

Результати

Проходить усі, крім тесту №6.
Результати див. На веб- сторінці http://codepad.org/k1NSoyQW

Пояснення

C # просто не може конкурувати з Ruby та Python за лаконічність, як завжди, але мені подобалося намагатися. Звичайно, є й інші значення, які працюватимуть так само добре (тобто початкове значення j = 999, а дільник = 277). Я вибрав їх після коротких експериментів.

З обгорткою для створення файлів

class R
{
    public static void Main(string[] args)
    {
        var r = new R();
        using (var f = new System.IO.FileStream(".\\out.bin", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.Read))
        using (var b = new System.IO.BinaryWriter(f))
        {
            for (long i = 0; i < 12 * 1024 * 1024; i += 4)
            {

                b.Write(r.N());
            }
        }
    }

    double j = 999;

    uint N()
    {
        uint i = 0, n = 0;
        for (; i++ < 4; n = n * 256 + (uint)j % 256)
            for (j /= 277; j < 100000; j *= j) ;
        return n;
    }

}

1

Пітона, 41/15 = 2,73333

v=0
def R():global v;v=hash(`v`);return v

Своєрідне обману за допомогою вбудованої хеш-функції, але вона є вбудованою, тому не більше шахрайства, ніж використання інших вбудованих, наприклад len. З іншого боку, болить мені за те, щоб заплатити за global v;заяву ...

Проходить усі тести Diehard (у мене була проблема з тестом №2, це SEGV на моїй машині OSX. Для моєї оцінки я припускаю, що це пройде).

Ось драйвер для створення файлу 16 Мб:

import sys
for i in xrange(1<<22):
  r=R()
  sys.stdout.write('%c%c%c%c'%(r&255, r>>8&255, r>>16&255, r>>24&255))

"Ця функція не повинна залучати до будь-яких бібліотек чи інших функцій, які також не були записані як частина програми, особливо виклики до / dev / random або вбудованої мовної rand () бібліотеки." Вибачте, але це дискваліфікує ваш запис.
Джо З.

Щоб було зрозуміло, "len" також дискваліфікує ваш запис.
Джо З.

Де ви малюєте лінію? Чи +є вбудована функція, а значить, дискваліфікована?
Кіт Рендалл

6
Але в багатьох мовах оператори та функції однакові. Дивіться +і __add__в python, або перевантаження оператора в c ++. Я знаю, що я ніби розщеплює волосся, тому розгляньте цей приклад. В Python я можу створити карту , як це: {'a':5}? Ви, мабуть, скажете "так", але потім вважайте, що під прикриттями вас hash('a')дзвонять, коли ви це зробите.
Кіт Рендалл

2
Я припускаю, що я б намалював лінію, коли потрібно синтаксично посилатися на функцію таким чином. Якщо ви знайдете хак в Python, який дозволить вам безпосередньо отримувати доступ до адреси карти без синтаксичного посилання на функцію "хеш", я можу прийняти її.
Джо З.

1

С, 38/15 = 2,533

long long x;f(){return(x+=x*x+9)>>32;}

Я не міг змусити тести Diehard працювати на моїй машині, але він передає набір PractRand для виходу до 8 ГБ, тому я припускаю, що він пропустить їх усі.


0

Брейн-Флак , 344 / (очікує на розгляд)

<>((()()){})<> push the amount of iterations to do for the PRNG
(((((((((((((((((((((((((((((((((((()()()){}()){})){}{}){()()()()({}[()])}{})){}{})){}{})()){}{})()){}{})){}{})){}{}){}())){}{})){}{})()){}{})()){}{})){}{})){}{})()){}{})()){}{}) push M (one of the values for the Blum Blum Shub PRNG
((((((((((((()()()){}){}){})){}{}){()({}[()])}{}){}())){}{})()){}{}) push s see above
<>{({}[()])<>starts the loop
(({({})({}[()])}{}) squares the current number
(<>))<>{(({})){({}[()])<>}{}}{}<>([{}()]({}))mods by M
<>}{}<>loop ends

Спробуйте в Інтернеті!

Це прекрасно працює, але всі посилання на тести твердих виправлень :( так що, поки ми не отримаємо нові, я не маю остаточного балу

Для цього використовується PRNG Blum Blum Shub, тому він повинен пройти більшість справ. Використовувані цифри досить великі, і в межах 16 МБ тестових зразків не з’явиться шаблонів


якщо це недійсно, скажіть мені
Крістофер

1
Я рахую 344. Теорема: Жодна повністю гольфна програма Brain-Flak не має непарну кількість байтів.
user202729

0

Завдання-С, 40/1 = 40

Досить розумний підхід, що експлуатується, .hashщоб дещо обдурити, але мені це подобається

for(int v=9;v=@(v).hash;printf("%i",v));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.