(완전 결정 론적) 의사 난수 비트 스트림 생성


11

당신의 손이 묶여있는 랜덤에서 영감을 얻었 습니다 .


목표

이 과제의 목표는 의사 랜덤 비트 스트림을 생성하는 프로그램을 작성하는 것입니다. 의사 랜덤 비트 스트림은 순전히 무작위 인 것처럼 보이지만 실제로는 결정적인 방식으로 생성되는 1과 0의 문자열입니다. 프로그램은 1과 0의 문자열 (선택적 공백)을 출력해야하며 다음 요구 사항을 통과해야합니다.

  1. 무제한 시간과 메모리가 주어지면 프로그램은 계속 1과 0의 문자열을 계속 출력해야합니다.
  2. 당신의 프로그램은 적당한 기계에서 약 1 분 안에 1000 개 이상의 랜덤 비트를 출력해야합니다. 이 요구 사항이 불가능하면 줄 이겠습니다.
  3. 비트 열은 반복 될 수 있지만 반복 구간의 길이는 1000 비트 이상이어야합니다.
  4. 비트 열은 무작위 테스트 (아래 설명 참조)를 가능한 한 많이 통과해야합니다.
  5. 프로그램은 외부 소스로부터 입력을 받거나 내장 rand ()와 유사한 함수를 사용해서는 안됩니다.
  6. 위의 요구 사항으로 인해 프로그램은 실행될 때마다 동일한 정확한 비트 문자열을 출력해야합니다.

무작위성 테스트 # 1

의사 랜덤 비트 열은 육안 검사시 명백한 패턴을 포함하지 않아야합니다.

무작위성 테스트 # 2 (주석에 따라 변경 될 수 있음)

비트 열은 1과 0의 등분 포를 포함해야합니다. 이것을 테스트하기 위해 (그리고 다른 것들도) 비트 스트림은 3 비트 길이의 세그먼트로 나뉩니다 101|111|001.

이 모든 세그먼트 중 1/8은 3이 1이고 0이 없어야하며, 3/8은 2가 1이고 1이 0, 3/8이 1과 2가 1, 2가 각각 1/8이어야합니다. 그들 중 1과 3은 없어야합니다.

무작위성 테스트 # 3

"실행"은 모두 동일한 값을 갖는 연속적인 일련의 비트로 정의됩니다. 문자열 1001001110에는 크기 1 ( 1..1.....0) 의 3 행 , 크기 2 ( .00.00....)의 2 행 및 크기 3 ( ......111.) 의 1 행이 있습니다 . 런은 겹치지 않습니다.

1000 개의 임의 비트의 문자열 중에서 크기가 1 인 250 개, 크기가 125 개의 2 개, 크기가 62 개의 3 개 등이 있어야합니다. 일반적으로 실행 크기 R 1000/(2**(R+1))의 경우 해당 크기의 실행이 있어야합니다.

무작위성 테스트 # 4

첫 번째 840 비트는 각각 420 비트의 두 반쪽으로 나뉩니다. 전반부의 각 비트는 후반의 해당 비트와 비교됩니다. 두 비트는 시간의 약 50 %와 일치해야합니다.


다음 은 테스트 2-4를 수행하는 Perl 프로그램의 소스 코드입니다. 현재로서는 비트 열에 공백이 없어야합니다.


객관적인 승리 기준 시간!

우승자는 6 가지 요구 사항과 모든 임의성 테스트를 임의성과 구분할 수없는 수준으로 통과시키는 프로그램입니다. 여러 프로그램이이 작업을 수행하면 반복하는 데 가장 오랜 시간이 걸리는 프로그램이 승리합니다. 여러 프로그램에서이 작업을 수행하는 경우 타이 브레이커 역할을하기 위해 더 많은 무작위 테스트를 찾아야 할 수 있습니다.


# 2와 # 3은 무작위성에 대한 좋은 기준은 아닙니다. 특히 # 2의 경우 무작위 샘플이이 특성을 나타내지 않을 수 있습니다. 더 큰 샘플 크기를 사용할 수 있습니까? 100에서 300 사이의 것을 제안하겠습니다.
Joel Cornett

비트 스트림의 큰 창에 대한 평균은 크게 변하지 않으며 0.5 정도 여야하므로 더 나은 측정 방법은 이동 평균입니다.
Joel Cornett

@JoelCornett 조언 감사합니다. 나는 무작위성 테스트에 대해 많이 모른다. # 2를 다른 것으로 바꾸고 이동 평균에 대해 읽고 있습니다.
PhiNotPi

1
문제 없어요. 임의의 시퀀스는 뭉쳐지고 균일하게 분포되지 않는 경향이 있으며, 이는 사기를 감지하기 위해 회계에 사용되는 경우가 있습니다. (난수에 대한 실수 균일 성을 발명 한 사람들이 종종 허위 숫자를 너무 고르게 분배 할 것입니다)
Joel Cornett

내장 된 암호화 기능 (AES 또는 SHA-2와 같은)을 사용할 수 있습니까?
코드 InChaos

답변:


8

C, 61

main(s,n){for(n=1u<<31;putchar((s%=n)/(n/2)&1|48);s*=65539);}

그래, 나는 그것이 코드 골프가 아니라는 것을 안다. 이것은 분명히 안티 솔루션입니다 ...하지만 충분히 당신의 기준을 충족합니다.

아웃 | 머리 -c840
$ ./a.out | 헤드 -c840 | perl tester.pl
테스트 2 : 1 (1) 2.93333333333333 (3) 3.1 (3) 0.966666666666667 (1)
테스트 3 : 214 99 71 24 7 5 1 1 2 2
테스트 4 : 0.495238095238095

기간은 2²⁹입니다.


6
이것은 존재하는 최악의 난수 생성기 중 하나 인 것으로 널리 알려진 것에서 임의성을 나타내는 것이 얼마나 어려운지를 보여줍니다. +1.
PhiNotPi

8

매스 매 티카 78 53 자

Pi의 이진 표현의 자릿수는 이것이 입증되지 않았지만 혼란스럽게 생성 된 것처럼 동작하는 것 같습니다.

다음의 간단한 루틴은 d10 진수에 해당하는 pi의 2 진수를 문자열로 결정적으로 반환합니다 .

f[d_]:=ToString@FromDigits@RealDigits[N[Pi,d],2][[1]]

용법

Pi의 십진수 301 자리를 요청하면 1000 개의 이진수를받습니다.

f[301]
StringLength[%]

(* out *)
1100100100001111110110101010001000100001011010001100001000110100110001001100011001100010100010111000000011011100000111001101000100101001000000100100111000001000100010100110011111001100011101000000001000001011101111101010011000111011000100111001101100100010010100010100101000001000011110011000111000110100000001001101110111101111100101010001100110110011110011010011101001000011000110110011000000101011000010100110110111110010010111110001010000110111010011111110000100110101011011010110110101010001110000100100010111100100100001011011010101110110011000100101111001111110110001101111010001001100010000101110100110100110001101111110110101101011000010111111111101011100101101101111010000000110101101111110110111101110001110000110101111111011010110101000100110011111101001011010111010011111001001000001000101111100010010110001111111100110010010010010100001100110010100011110110011100100010110110011110111000010000000000111110010111000101000010110001110111111000001011001100011011010010010000011011000011100011

1000 (* characters *)

Pi는 비합리적인 숫자이므로 마침표가 없습니다. 그러나 실행중인 하드웨어로 인해 실질적인 제약이 따릅니다.

시험 1 나에게 좋아 보인다.

시험 2

d=301;
Partition[RealDigits[N[Pi,d],2][[1]],{3}];
Tally[%]
(* out *)
{{{1,1,0},35},{{0,1,0},45},{{0,0,0},41},{{1,1,1},40},
{{0,1,1},50},{{1,0,1},32},{{1,0,0},43},{{0,0,1},47}}

보다 철저한 점검 :

d=10^6;
Partition[RealDigits[N[Pi,d],2][[1]],{3}];
Tally[%]

{{{1,1,0},138565},{{0,1,0},138146},{{0,0,0},138260},{{1,1,1},138427},
{{0,1,1},139119}, {{1,0,1},138404},{{1,0,0},137926},{{0,0,1},138462}}

테스트 3 : 런

d=10^6;
res3=SortBy[Tally@Split@RealDigits[N[Pi,d],2][[1]],Last]/.{a_,b_}:> {Length[a],b}
ListPlot[res3 ,AxesLabel-> {"Run Length","Runs"},AxesOrigin->{0,0}]

나는 런 분포를 체계적으로 점검하기 위해 많은 경우를 실행했다. 약 3 백만 개의 이진수에는 830k 런 1, 416k 런 2, 208k 런 3, 104k 런 4 등이있었습니다.

런 2 테스트 4 : 데이터의 전반과 후반의 일치

일치는 0과 2의 212 가지 경우입니다. 불일치는 각각의 숫자의 합이 1 인 208 경우입니다.

d=301;
Tally[Plus@@Partition[Take[RealDigits[N[Pi,d],2][[1]],840],420]]

(* out *)
{{1,208},{0,108},{2,104}}

타이밍

3321928 이진수 (10 ^ 6 10 진수에 해당)를 계산하는 데 2 ​​초 미만이 걸립니다.

(r=f[10^6]);//AbsoluteTiming
StringLength[r]

(*out*)
{1.785928,Null}    
3321928

1
나는 누군가가 이것을 할 것이라는 것을 알았다.
카운터 시계를 돌리는 것을 중단했다.

1
매달린 과일, 그렇지?
DavidC

1 바이트를 저장 하는 e대신 사용할 수 pi없습니까?
pppery December

되어 e무질서하게 분산?
DavidC

3

파이썬, 90

g=[19]
print(''.join("01"[(g.append((11*g[-1]+13)%1024)or g[-1])>512]for i in range(1000)))

g시드 값입니다. 랜덤 샘플링은 평균 평균 0.506및 표준 편차 .0473(샘플 크기 1000)를 산출 한 샘플 평균의 랜덤 샘플링을 반복하여 현저하게 정규 분포를 나타냅니다 . 불행하게도, 무작위성은 초기 시드에 매우 민감합니다. 위 코드의 씨앗은 나에게 최고의 무작위성을 주었다.

최신 정보

이 코드가 OP의 테스트를 어떻게 유지하는지 봅시다 :

테스트 # 1

이것은 약간 주관적이지만 ... 나에게 불규칙하게 보입니다.

테스트 # 2

3 대 1 : 0.141
2 대 1 : 0.371
1 대 1 : 0.353
제로 1 : 0.135

테스트 # 3

크기별로 실행 :

8: 11
7: 3
6: 7
5: 13
4: 32
3: 67
2: 119
1: 216

테스트 # 4

평등의 비율 : 0.94 이것은 오타입니다. 곧 올바른 번호로 업데이트됩니다.


1
'for'전에 공백을 제거 할 수 있습니다.
daniero

2

하스켈 74 58

main=print$iterate(read.take 9.show.(^3))7>>=show.(`mod`2)

단순화를위한 shiona 에게 감사합니다 . 결과 :

/ 의사 난수 | 머리 -c 1000

./pseudorandom | 헤드 -c 1000 | 펄 test.pl

테스트 2 : 0.966666666666667 (1) 2.4 (3) 3.3 (3) 1.33333333333333 (1)

테스트 3 : 260108 66 33 15 11 5 2

테스트 4 : 0.495238095238095

이것은 또한 끔찍한 의사 랜덤 생성기입니다 (von-Neuman이 사용하는 것과 유사). 모르는 사람들 concatMap == (=<<) == flip . (>>=)(목록)


당신은 대체 할 수 있습니다 \x->if odd x then"1"else"0"show.(`mod`2).
shiona

1

문제는 본질적으로 "스트림 암호 구현"과 동일합니다. RC4는 비교적 단순하기 때문에 구현합니다.

RC4의 시작 부분이 비트 편향되어 있기 때문에 키를 사용하지 않고 처음 100000 비트를 삭제합니다. 특히 키 일정을 건너 뛰었 기 때문입니다. 그러나 나는 그것 없이도 테스트를 통과 할 것으로 기대합니다 (20 자 코드 절약).

일반적으로 하나의 사이클 당 전체 바이트를 출력하지만 바이너리로 변환하는 것은 C #에서 추악하기 때문에 가장 중요한 비트를 제외한 모든 것을 버립니다.

var s=Enumerable.Range(0,256).ToArray();
byte i=0,j=0;
for(int k=0;;k++)
{
    i++;
    j+=(byte)s[i];
    var t=s[i];s[i]=s[j];s[j]=t;
    if(k>99999)
        Console.Write(s[i]+s[j]&1);
}

또는 공백없이 :

var s=Enumerable.Range(0,256).ToArray();byte i=0,j=0;for(int k=0;;k++){i++;j+=(byte)s[i];var t=s[i];s[i]=s[j];s[j]=t;if(k>99999)Console.Write(s[i]+s[j]&1);}

C # (156 자)은 LinqPad의 명령문 모드에서 작동합니다. 전체 C # 프로그램의 경우 일반적인 상용구를 추가하십시오.


내장 된 암호화 프리미티브 (Cheater solution)를 사용할 수도 있습니다.

var h=SHA256.Create();for(BigInteger i=0;;i++){Console.Write(h.ComputeHash(i.ToByteArray())[0]%2);}

(C #, 99 자, LinqPad의 명령문 모드에서 작동합니다. 일반 C # 컴파일러의 경우 약간의 상용구를 추가해야합니다)

암호화 해시 함수의 출력은 임의의 데이터와 구별 할 수 없도록 설계되었으므로 던지는 모든 임의성 테스트 (더 어려워 ...)를 통과 할 것으로 기대하지만 테스트하기에는 너무 게으 릅니다.


1

C, 52 자

main(a){for(a=1;putchar(48+a%2);a=a/2^-(a%2)&576);}

이것은 10 비트 LFSR 테스트 결과입니다.

$ ./a.out |head -c 1000 | perl randtest.pl
Test 2: 1.13333333333333 (1) 2.86666666666667 (3) 3.16666666666667 (3) 0.833333333333333 (1)
Test 3:  251 122 64 32 16 8 4 2  1
Test 4: 0.466666666666667

a1로 시작해야합니다 (인수없이 호출되었다고 가정). 또한 당신은 스틱 수 a=, 중간에 같은 a=a/2^-!putchar(49-a%2)%576(알고리즘과 약간의 자유를 복용)
walpen

@walpen : 초기 구현이 설정되지 않았습니다. a" The program must not take any input from any external sources" 때문에 변경되었습니다.
Hasturkun

1

세이지 / 파이썬

이 프로그램은 3 3 3 3 형식의 충분히 높은 지수 탑에 공통 인 가장 오른쪽 이진수를 인쇄합니다 . . . 실현 가능하게 생성 될 수있는 모든 것에서 이들은 Graham 수의 가장 오른쪽 이진수 입니다. 숫자 순서는 무한하며 주기적이 아닙니다.

m = 1; x = 3; last = 0
while True:
    m *= 2; x = pow(3,x,m); l = len(bin(x))
    print '1' if l > last else '0',
    last = l

1000 자리 숫자의 경우 2 초 미만이 걸렸습니다. 그러나 시간은 자릿수에서 선형보다 훨씬 빠르게 증가합니다.

영업의 프로그램을 사용하여 테스트 결과 입니다

Test 2: 1.26666666666667 (1) 3.16666666666667 (3) 2.8 (3) 0.766666666666667 (1)
Test 3:  268 126 61 30 20 7 2  1 1
Test 4: 0.466666666666667

( 32000 자 이상의 숫자 및 추가 통계 테스트 는 G의 가장 오른쪽 자릿수가 무작위입니까? 를 참조하십시오.)


1

자바, 371 317

128 비트 LFSR 기반 (비트 탭은 xilinx 애플리케이션 노트 52에서 제공 )

편집 : BigInteger 사용에 만족하지 않아서이 버전은 사용하지 않습니다. 일부 문자를 저장했습니다. 좋은 '시딩'방법을 생각할 수 없으므로 출력이 조금 덜 무작위 일 수 있습니다.

새 코드 : 인수 : BITS_TO_PRINT

class R{public static void main(String[]a){int L=65536;int[]v={0,128,126,101,99};int[]b=new int[L];for(int x=0;x<L;x++)b[x]=(x*x)&1;for(int i=0;i<Integer.parseInt(a[0])+L;i++){if(1!=(b[v[1]]^b[v[2]]^b[v[3]]^b[v[4]]))b[v[0]]=1;else b[v[0]]=0;if(i>L)System.out.print(b[v[0]]);for(int j=0;j<5;j++)v[j]=(v[j]-1)&(L-1);}}}

이전 버전 : 인수 : SEED, BITS_TO_PRINT

import java.math.BigInteger;class R{public static void main(String[]a){BigInteger v=new BigInteger(a[0]);BigInteger m=new BigInteger("ffffffffffffffffffffffffffffffff",16);for(int i=Integer.parseInt(a[1]);i>0;i--){v=v.shiftLeft(1);if(!(v.testBit(128)^v.testBit(126)^v.testBit(101)^v.testBit(99))){v=v.setBit(0);}v=v.and(m);java.lang.System.out.print(v.testBit(0)?1:0);}}}

새 버전 : 예제 출력, 비트 = 100 :

011001100111000110010100100111011100100111000111001111110110001001100000100111111010111001100100011

1
BTW, 나는이 게시물의 두 노아 계정이 같은 사람이라고 가정합니다. 그렇다면 meta.codegolf.stackexchange.com 에서 중재자에게 병합을 요청할 수 있습니다.
Peter Taylor

0

JavaScript-1000 의사 난수 비트의 경우 1ms ~ 2ms (100000 비트의 경우 139ms ~ 153ms)

이 솔루션은 제곱근이 비이성적이고 거의 임의적이라는 사실을 사용합니다. 기본적으로 시작하려면 2의 제곱근이 필요하고 이진수로 변환하고 이전 루트와 일치하는 선행 부분을 버리고 임의의 문자열에 추가하고 다음 더 높은 숫자로 반복합니다 (또는 숫자가 반복되면 2로 되돌아갑니다) 30 비트 이상이어야 함) 임의 문자열이 충분히 길면이를 반환합니다.

var getDeterministicPseudoRandString = function(length){
    var randString = '';

    var i = 2;
    var prevRand = '';

    outerLoop:
    while(randString.length < length){
        var nextRand, nextFullRand = Math.sqrt(i++).toString(2).substring(1).replace('.', '');
        nextRand = nextFullRand;
        for(var j = prevRand.length; j > 0; j--){
            var replaceString = prevRand.substring(0, j);

            nextRand = nextFullRand;

            if(nextFullRand.indexOf(replaceString) == 0){
                if(j == prevRand.length && j > 30){
                    //start i over at 2
                    console.log('max i reached: ' + i);

                    i = 2;
                    continue outerLoop;
                } else {
                    nextRand = nextFullRand.replace(replaceString, '');
                }

                break;
            }
        }
        prevRand = nextFullRand;

        randString += nextRand;
    }

    return randString.substring(0, length);//Return the substring with the appropriate length
};

아직 테스트를 수행하지는 않았지만 테스트에서 잘 수행 될 것이라고 생각합니다. 여기에 바이올린 이있어서 실제로 볼 수 있습니다. 내 시대에는 방금 프로그램을 여러 번 실행했으며 범위로 가장 빠르고 느린 값을 사용했습니다.


0

파이썬

import hashlib
x=''
while 1:
    h=hashlib.sha512()
    h.update(x)
    x=h.digest()
    print ord(x[0])%2

약 2 ^ 512의 기간이 있어야합니다.


0

펄, 44 바이트

나는 이것이 코드 골프가 아니라는 것을 알고 있지만, 나는 항상 간단한 이차 함수의 하위 비트를 취하는 팬이었습니다.

$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1

기간이 30 억을 초과하지만 더 계산할 디스크 공간이 부족합니다.


1
숫자 상수와 키워드를 병치하고 다음을 분배하여 3 개의 문자를 절약 할 수 있습니다.$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1
ardnew
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.