Tạo luồng bit giả ngẫu nhiên (hoàn toàn xác định)


11

Lấy cảm hứng từ Random với đôi tay bị trói :


Mục đích

Mục tiêu của thử thách này là viết một chương trình tạo ra luồng bit giả ngẫu nhiên, là một chuỗi 1 và 0 dường như hoàn toàn ngẫu nhiên, nhưng thực sự được tạo ra theo cách xác định. Chương trình của bạn sẽ xuất ra một chuỗi 1 và 0 (với khoảng trắng tùy chọn) và phải vượt qua các yêu cầu sau:

  1. Với thời gian và bộ nhớ không giới hạn, chương trình của bạn phải tiếp tục xuất ra chuỗi 1 và 0 mãi mãi
  2. Chương trình của bạn phải xuất hơn 1000 bit ngẫu nhiên trong khoảng một phút, trên một máy hợp lý. Nếu yêu cầu này là không thể, thì tôi sẽ giảm bớt nó.
  3. Chuỗi bit có thể lặp lại, nhưng độ dài của phần lặp lại phải lớn hơn 1000 bit.
  4. Chuỗi bit phải vượt qua càng nhiều phép thử ngẫu nhiên (được mô tả bên dưới) càng tốt.
  5. Chương trình không được lấy bất kỳ đầu vào nào từ bất kỳ nguồn bên ngoài nào hoặc sử dụng bất kỳ hàm rand () giống như tích hợp nào.
  6. Do yêu cầu trên, chương trình phải xuất ra cùng một chuỗi bit chính xác mỗi lần chạy.

Kiểm tra ngẫu nhiên # 1

Chuỗi bit giả ngẫu nhiên không được bao gồm bất kỳ mẫu rõ ràng nào khi kiểm tra trực quan.

Kiểm tra ngẫu nhiên # 2 (có thể thay đổi dựa trên ý kiến)

Chuỗi bit phải chứa phân phối bằng 1s và 0. Để kiểm tra điều này (và những thứ khác nữa), luồng bit được chia thành các đoạn dài 3 bit, chẳng hạn như 101|111|001.

Trong số tất cả các phân khúc này, 1/8 trong số chúng nên có ba 1 và không 0, 3/8 trong số chúng nên có hai 1 và một 0, 3/8 trong số chúng nên có một 1 và hai 0 và 1/8 trong số họ không nên có 1 và ba 0.

Kiểm tra ngẫu nhiên # 3

Một "run" được định nghĩa là một chuỗi bit liên tiếp mà tất cả đều có cùng giá trị. Chuỗi 1001001110có ba lần chạy kích thước 1 ( 1..1.....0), hai lần chạy kích thước 2 ( .00.00....) và một lần chạy kích thước 3 ( ......111.). Lưu ý rằng chạy không chồng chéo.

Trong chuỗi 1000 bit ngẫu nhiên, cần có khoảng 250 lần chạy kích thước 1, 125 lần chạy kích thước 2, 62 lần chạy kích thước 3, v.v. Nói chung, đối với kích thước chạy R, cần có khoảng 1000/(2**(R+1))chạy với kích thước đó.

Kiểm tra ngẫu nhiên # 4

8 bit đầu tiên được chia thành hai nửa 420 bit mỗi bit. Mỗi bit trên nửa đầu được so sánh với bit tương ứng trên nửa thứ hai. Hai bit sẽ khớp với khoảng năm mươi phần trăm thời gian.


Đây là mã nguồn của chương trình Perl thực hiện các thử nghiệm từ 2 đến 4. Cho đến nay, nó yêu cầu chuỗi bit không chứa bất kỳ khoảng trắng nào.


Tiêu chí chiến thắng khách quan Thời gian!

Người chiến thắng là chương trình vượt qua tất cả 6 yêu cầu và tất cả các bài kiểm tra ngẫu nhiên đến mức độ không thể phân biệt được với tính ngẫu nhiên. Nếu nhiều chương trình hoàn thành việc này, thì chương trình nào mất nhiều thời gian nhất để lặp lại sẽ giành chiến thắng. Nếu nhiều chương trình thực hiện điều này, thì tôi có thể phải tìm thêm các thử nghiệm ngẫu nhiên để hoạt động như các bộ ngắt kết nối.


# 2 và # 3 không thực sự là tiêu chí rất tốt cho sự ngẫu nhiên. Đối với # 2 đặc biệt, một mẫu ngẫu nhiên có thể sẽ không thể hiện đặc tính này. Có lẽ bạn có thể làm một kích thước mẫu lớn hơn? Tôi sẽ đề xuất một cái gì đó trong khoảng từ 100 đến 300.
Joel Cornett

Phương pháp đo tốt hơn sẽ là trung bình di động, vì giá trị trung bình trên một cửa sổ lớn trên dòng bit sẽ không thay đổi nhiều (và nên ở khoảng 0,5)
Joel Cornett

@JoelCornett Cảm ơn lời khuyên. Tôi không biết nhiều về các bài kiểm tra ngẫu nhiên. Tôi sẽ thay đổi số 2 thành một thứ khác và tôi đang đọc về việc di chuyển trung bình.
PhiNotPi

1
Không vấn đề gì. Trình tự ngẫu nhiên có xu hướng co cụm và không được phân phối đồng đều, đây là một thực tế đôi khi được sử dụng trong kế toán để phát hiện gian lận. (Số gian lận thường sẽ được phân phối quá đồng đều, bởi vì những người phát minh ra chúng đồng nhất sai lầm về tính ngẫu nhiên)
Joel Cornett

Tôi có thể sử dụng các chức năng mã hóa được xây dựng (chẳng hạn như AES hoặc SHA-2) không?
CodeInChaos

Câu trả lời:


8

C, 61

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

Vâng, tôi biết đó không phải là mã golf. Đây rõ ràng là một giải pháp chống ... nhưng nó chắc chắn đáp ứng đủ các tiêu chí của bạn.

ra | đầu -c840
$ ./a.out | đầu -c840 | perl test.pl
Test 2: 1 (1) 2.93333333333333 (3) 3.1 (3) 0.966666666666667 (1)
Kiểm tra 3: 214 99 71 24 7 5 1 1 2 2
Kiểm tra 4: 0.495238095238095

Thời gian là 2²⁹.


6
Điều này cho thấy mức độ khó để nói ngẫu nhiên từ một thứ được biết đến rộng rãi là một trong những bộ tạo số ngẫu nhiên tồi tệ nhất trong sự tồn tại. +1.
PhiNotPi

8

Toán học 78 53 ký tự

Các chữ số của biểu diễn nhị phân của Pi dường như hoạt động như thể chúng được tạo ra một cách hỗn loạn mặc dù điều này chưa được chứng minh.

Các thường trình đơn giản sau đây trả về một cách xác định dưới dạng một chuỗi các chữ số nhị phân của pi, tương ứng với các dchữ số thập phân:

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

Sử dụng

Nếu chúng tôi yêu cầu bản sao của 301 chữ số thập phân của Pi, chúng tôi sẽ nhận được 1000 chữ số nhị phân.

f[301]
StringLength[%]

(* out *)
1100100100001111110110101010001000100001011010001100001000110100110001001100011001100010100010111000000011011100000111001101000100101001000000100100111000001000100010100110011111001100011101000000001000001011101111101010011000111011000100111001101100100010010100010100101000001000011110011000111000110100000001001101110111101111100101010001100110110011110011010011101001000011000110110011000000101011000010100110110111110010010111110001010000110111010011111110000100110101011011010110110101010001110000100100010111100100100001011011010101110110011000100101111001111110110001101111010001001100010000101110100110100110001101111110110101101011000010111111111101011100101101101111010000000110101101111110110111101110001110000110101111111011010110101000100110011111101001011010111010011111001001000001000101111100010010110001111111100110010010010010100001100110010100011110110011100100010110110011110111000010000000000111110010111000101000010110001110111111000001011001100011011010010010000011011000011100011

1000 (* characters *)

Bởi vì Pi là một số vô tỷ, không có dấu chấm. Tuy nhiên, sẽ có những hạn chế thực tế do phần cứng đang chạy.

Bài kiểm tra 1 Có vẻ tốt với tôi.

Kiểm tra 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}}

Kiểm tra kỹ hơn:

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

Bài kiểm tra 3: Chạy

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}]

Tôi đã chạy một số lượng lớn các trường hợp để kiểm tra một cách có hệ thống việc phân phối các lần chạy. Trong khoảng 3 triệu chữ số nhị phân, đã có 830k chạy 1, 416k chạy 2, 208k chạy 3, 104k chạy 4, v.v.

chạy 2 Kiểm tra 4: Kết hợp nửa đầu và nửa sau của dữ liệu

Các trận đấu là 212 trường hợp 0 ​​và 2; sự không phù hợp là 208 trường hợp trong đó tổng các chữ số tương ứng là 1.

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

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

Thời gian

Phải mất dưới hai giây để tính toán 3321928 chữ số nhị phân (tương ứng với 10 ^ 6 chữ số thập phân).

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

(*out*)
{1.785928,Null}    
3321928

1
Tôi biết ai đó sẽ làm điều này ...
đã ngừng quay ngược lại vào

1
Trái cây treo thấp, phải không?
DavidC

Bạn không thể sử dụng ethay vì piđể lưu một byte?
pppery

Được ephân phối hỗn loạn?
DavidC

3

Con trăn, 90

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

glà giá trị hạt giống. Lấy mẫu ngẫu nhiên thể hiện sự phân phối bình thường đáng kể lặp lại lấy mẫu ngẫu nhiên của mẫu có nghĩa là mang lại giá trị trung bình 0.506và độ lệch chuẩn của aa .0473(cỡ mẫu 1000). Thật không may, tính ngẫu nhiên rất nhạy cảm với hạt giống ban đầu. Hạt giống trong đoạn mã trên đã cho tôi sự ngẫu nhiên tốt nhất: p

CẬP NHẬT

Hãy xem cách mã này tuân theo các thử nghiệm của OP:

Bài kiểm tra số 1

Điều này hơi chủ quan ... nhưng nó có vẻ khá bất thường đối với tôi.

Bài kiểm tra số 2

Ba 1's: 0.141
Hai 1's: 0.371
Một 1: 0.353
Zero 1's: 0.135

Bài kiểm tra số 3

Chạy theo kích thước:

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

Bài kiểm tra số 4

Tỷ lệ tương đương: 0,94 Đây là một lỗi đánh máy. Sẽ cập nhật với số chính xác sớm.


1
Bạn có thể xóa khoảng trắng trước 'for'.
daniero

2

Haskell 74 58

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

Cảm ơn shiona đã đơn giản hóa. Các kết quả:

/ giả danh | đầu -c 1000

./pseudorandom | đầu -c 1000 | perl test.pl

Kiểm tra 2: 0.966666666666667 (1) 2.4 (3) 3.3 (3) 1.33333333333333 (1)

Kiểm tra 3: 260 108 66 33 15 11 5 2

Kiểm tra 4: 0.495238095238095

Đây cũng là một trình tạo giả ngẫu nhiên khủng khiếp (tương tự như một công cụ được sử dụng bởi von-Neuman). Đối với những người không biết concatMap == (=<<) == flip . (>>=)(cho danh sách)


Bạn có thể thay thế \x->if odd x then"1"else"0"bằng show.(`mod`2).
shiona

1

Câu hỏi về cơ bản tương đương với "thực hiện mật mã luồng". Vì vậy, tôi thực hiện RC4, vì nó tương đối đơn giản.

Tôi không sử dụng khóa nào và bỏ 100000 bit đầu tiên, vì phần đầu của RC4 hơi sai lệch, đặc biệt là khi tôi bỏ qua lịch trình khóa. Nhưng tôi hy vọng nó sẽ vượt qua bài kiểm tra của bạn ngay cả khi không có điều đó (tiết kiệm 20 ký tự mã).

Thông thường người ta sẽ tạo ra một byte đầy đủ trên mỗi chu kỳ, nhưng chuyển đổi sang nhị phân khá xấu trong C #, vì vậy tôi chỉ cần loại bỏ mọi thứ trừ bit quan trọng nhất.

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

Hoặc không có dấu cách:

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 ký tự, hoạt động ở chế độ câu lệnh của LinqPad. Đối với chương trình C # đầy đủ, hãy thêm bản tóm tắt thông thường.


Chúng tôi cũng có thể sử dụng các loại tiền điện tử được tích hợp sẵn (giải pháp Cheater):

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

(C #, 99 ký tự, hoạt động ở chế độ câu lệnh của LinqPad. Đối với trình biên dịch C # bình thường, bạn sẽ cần thêm một chút bản tóm tắt)

Đầu ra của các hàm băm mật mã được thiết kế để không thể phân biệt được với dữ liệu ngẫu nhiên, vì vậy tôi hy vọng nó sẽ vượt qua tất cả các bài kiểm tra ngẫu nhiên (khó hơn, ...) bạn ném vào nó, nhưng tôi quá lười để kiểm tra.


1

C, 52 ký tự

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

Đây là LFSR 10 bit, kết quả kiểm tra:

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

anên bắt đầu là 1, (giả sử nó được gọi không có đối số). Ngoài ra, bạn có thể dán a=ở giữa, một cái gì đó như a=a/2^-!putchar(49-a%2)%576(lấy một số quyền tự do với thuật toán)
walpen

@walpen: Việc triển khai ban đầu của tôi không được đặt a, tôi đã thay đổi vì " The program must not take any input from any external sources"
Hasturkun

1

Hiền nhân / Python

Chương trình này in các chữ số nhị phân ngoài cùng bên phải phổ biến cho mọi tháp lũy thừa đủ cao của mẫu 3 3 3 3 . . . Đối với tất cả những gì có thể được tạo ra một cách khả thi, đây là những chữ số nhị phân ngoài cùng bên phải của số Graham . Trình tự chữ số là vô hạn, và không phải là định kỳ.

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

Đối với 1000 chữ số, điều này mất ít hơn 2 giây; tuy nhiên, thời gian sẽ tăng nhanh hơn nhiều so với tuyến tính về số lượng chữ số.

Các kết quả thử nghiệm sử dụng chương trình của 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

(Xem Các chữ số ngoài cùng bên phải của G có ngẫu nhiên không? Cho hơn 32000 chữ số và các kiểm tra thống kê bổ sung.)


1

Java, 371 317

Dựa trên LFSR 128 bit (vòi bit là từ ghi chú ứng dụng xilinx 52 )

EDIT: Tôi không hài lòng với việc sử dụng BigInteger nên phiên bản này không có. Lưu một số nhân vật. Đầu ra có thể ít ngẫu nhiên hơn một chút vì tôi không thể nghĩ ra phương pháp 'gieo hạt' tốt.

Mã mới: Đối số: 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);}}}

Phiên bản cũ: Đối số: 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);}}}

Phiên bản mới: Đầu ra ví dụ, bits = 100:

011001100111000110010100100111011100100111000111001111110110001001100000100111111010111001100100011

1
BTW, tôi cho rằng cả hai tài khoản của Nô-ê từ bài đăng này là cùng một người. Nếu vậy, bạn có thể yêu cầu người điều hành hợp nhất chúng tại meta.codegolf.stackexchange.com
Peter Taylor

0

JavaScript - 1ms đến 2ms cho 1000 bit giả ngẫu nhiên (139ms đến 153ms cho 100000 bit)

Giải pháp này sử dụng thực tế là căn bậc hai là không hợp lý, và do đó khá nhiều ngẫu nhiên. Về cơ bản, nó lấy căn bậc 2 của 2 để bắt đầu, chuyển đổi nó thành nhị phân, ném ra phần đầu khớp với gốc trước đó, nối nó với chuỗi ngẫu nhiên, lặp lại với số cao hơn tiếp theo (hoặc quay lại 2 nếu số lặp lại và dài ít nhất 30 bit) và trả về chuỗi ngẫu nhiên một khi nó đủ dài.

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

Tôi chưa chạy nó qua các bài kiểm tra, nhưng tôi tưởng tượng nó sẽ làm tốt với họ. Đây là một câu đố để bạn có thể thấy nó trong hành động. Đối với thời gian của tôi, tôi chỉ chạy chương trình nhiều lần và lấy các giá trị nhanh nhất và chậm nhất làm phạm vi.


0

Con trăn

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

Nên có khoảng thời gian khoảng 2 ^ 512.


0

perl, 44 byte

Tôi biết đây không phải là mã golf, nhưng tôi luôn là người thích lấy các bit bậc thấp của hàm bậc hai đơn giản, ví dụ:

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

Thời gian dài hơn 3 tỷ, nhưng tôi đã hết dung lượng đĩa để tính toán thêm.


1
bạn có thể lưu 3 ký tự bằng cách ghép các hằng số và từ khóa và cũng phân phối 4:$x=1/7;print substr($x*=4-4*$x,9,1)%2while 1
ardew
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.