(完全に決定的な)擬似ランダムビットストリームを生成する


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には1が3つあり、0はありません。3/ 8には2つの1と1つの0があり、3/8には1と2つの0があり、1/8がありますそれらのうち、1がなく、3つの0が必要です。

ランダム性テスト#3

「実行」は、すべて同じ値を持つ連続した一連のビットとして定義されます。文字列に1001001110は、サイズ1(1..1.....0)の3 つの実行、サイズ2(.00.00....)の2 つの実行、およびサイズ3(......111.)の1つの実行があります。実行が重複しないことに注意してください。

1000個のランダムビットの文字列のうち、サイズ1の実行が約250回、サイズ2の実行が125回、サイズ3の実行が62回などである必要があり1000/(2**(R+1))ます。

ランダム性テスト#4

最初の840ビットは、それぞれ420ビットの2つの半分に分割されます。前半の各ビットは、後半の対応するビットと比較されます。2つのビットは、約50%の時間で一致する必要があります。


以下は、テスト2〜4を実行するPerlプログラムのソースコードです。現在のところ、ビットの文字列に空白が含まれていないことが必要です。


客観的勝利基準時間!

勝者は、6つの要件すべてとすべてのランダム性テストに合格し、ランダム性と区別できない程度に合格するプログラムです。複数のプログラムがこれを達成する場合、繰り返しに最も長い時間がかかるプログラムが勝ちます。複数のプログラムがこれを達成する場合、タイブレーカーとして機能するために、さらにランダム性テストを見つける必要があるかもしれません。


#2と#3は、ランダム性の基準としてはあまり良くありません。特に#2については、ランダムなサンプルはおそらくこの特性を示さないでしょう。たぶん、あなたはより大きなサンプルサイズを行うことができますか?私は100と300との間に何かを示唆している
ジョエル・コルネット

より良い測定方法は、移動平均です。ビットストリームの大きなウィンドウの平均はあまり変化しないため(0.5程度でなければなりません)
ジョエルコルネット

@JoelCornettアドバイスをありがとう。ランダム性テストについてはあまり知りません。#2を別のものに変更し、移動平均について読んでいます。
PhiNotPi

1
問題ない。ランダムシーケンスは、まとまりがあり、均一に分散されない傾向があります。これは、不正行為を検出するためにアカウンティングで使用されることがあるという事実です。(不正な番号は、しばしば均等に分散されます。なぜなら、それらを発明した人々は、ランダム性のために均一性を間違えるからです)
ジョエル・コーネット

組み込みの暗号化機能(AESやSHA-2など)を使用できますか?
CodesInChaos

回答:


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つであることが広く知られているものからランダム性を判断するのがどれほど難しいかを示しています。+1。
PhiNotPi

8

Mathematica 78 53文字

Piのバイナリ表現の数字は、証明されていないものの、カオス的に生成されているかのように動作するようです。

次の簡単なルーチンは、d10進数に対応するpiの2進数を確定的に文字列として返します。

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

使用法

Piの301の10進数の桁を要求すると、1000の2進数を受け取ります。

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百万の2進数では、1の830kラン、2の416kラン、3の208kラン、4の104kランなどがありました。

実行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の2進数(10 ^ 6 10進数に相当)の計算には2秒未満かかります。

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

(*out*)
{1.785928,Null}    
3321928

1
私はこれを行うだろう、誰かが...知っていた
counterclockwis回すために中止した

1
垂れ下がった果物ですよね?
DavidC

1バイトを保存するe代わりに使用できませんpiか?
pppery

されるeカオス的に分散?
DavidC

3

Python、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)が得られたサンプル平均値のランダムサンプリングが繰り返される非常に正規分布が見られます。残念ながら、ランダム性は初期シードに非常に敏感です。上記のコードのシードは、最高のランダム性を与えてくれました:p

更新

このコードが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に感謝します。結果:

/ pseudorandom | ヘッド-c 1000

./pseudorandom | ヘッド-c 1000 | perl 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)
潮ona

1

この質問は、「ストリーム暗号を実装する」と本質的に同等です。そのため、RC4は比較的単純なので、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#プログラムの場合、通常のボイラープレートを追加します。


組み込みの暗号プリミティブを使用することもできます(チートソリューション):

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のすべての十分に高い累乗タワーに共通する右端の2進数を出力します。。実行可能であれば、これらはGrahamの数値の右端の2進です。数字列は無限であり、周期的ではありません。

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秒未満で完了しました。ただし、桁数が直線的に増加するよりもはるかに速く時間が増加します。

OPのプログラムを使用して、テスト結果があります

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

Java、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
ところで、この投稿のノアのアカウントは両方とも同じ人物だと思います。その場合は、meta.codegolf.stackexchange.com
ピーターテイラー

0

JavaScript-1000疑似ランダムビットの場合は1ミリ秒から2ミリ秒(100000ビットの場合は139ミリ秒から153ミリ秒)

このソリューションは、平方根が非合理的であり、したがってほぼランダムであるという事実を使用しています。基本的に、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

perl、44バイト

私はこれがゴルフのコードではないことを知っていますが、私は常に単純な二次関数の低次ビットを取ることのファンでした。例えば:

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

期間は30億を超えていますが、より多くを計算するためにディスク容量が不足しています。


1
あなたは数値定数とキーワードを並置しても分散することにより、3つの文字を保存することができ、その4:$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1
ardnew
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.