PRNG để tạo số với n bit chính xác


12

Tôi hiện đang viết một số mã để tạo dữ liệu nhị phân. Tôi đặc biệt cần tạo các số 64 bit với một số bit đã cho; chính xác hơn, quy trình sẽ lấy một số 0<n<64 và trả về số 64 bit giả ngẫu nhiên với chính xác bit được đặt thành và phần còn lại được đặt thành 0.n1

Cách tiếp cận hiện tại của tôi liên quan đến một cái gì đó như thế này:

  1. Tạo một số giả 64 bit .k
  2. Đếm các bit trong , lưu trữ kết quả trong .kb
  3. Nếu , đầu ra ; mặt khác đi đến 1.b=nk

Điều này hoạt động, nhưng nó có vẻ không phù hợp. Có một số loại thuật toán PRNG có thể tạo ra các số với tập bit thanh lịch hơn số này không?n

Câu trả lời:


12

Những gì bạn cần là một số ngẫu nhiên trong khoảng từ 0 đến . Vấn đề sau đó là biến điều này thành mô hình bit.(64n)1

Đây được gọi là mã hóa liệt kê và nó là một trong những thuật toán nén được triển khai lâu đời nhất. Có lẽ thuật toán đơn giản nhất là từ Thomas Cover. Điều này dựa trên quan sát đơn giản rằng nếu bạn có một từ dài bit, trong đó các bit được đặt là theo thứ tự bit có ý nghĩa nhất, thì vị trí của từ này theo thứ tự từ vựng của tất cả các từ có thuộc tính này Là:x k ... x 1nxkx1

1ik(xii)

Vì vậy, ví dụ, đối với một từ 7 bit:

i(0000111)=(23)+(12)+(01)=0
i(0001011)=(33)+(12)+(01)=1
i(0001101)=(33)+(22)+(01)=2

...và như thế.

Để có được mẫu bit từ thứ tự, bạn chỉ cần giải mã lần lượt từng bit. Một cái gì đó như thế này, trong một ngôn ngữ như C:

uint64_t decode(uint64_t ones, uint64_t ordinal)
{
    uint64_t bits = 0;
    for (uint64_t bit = 63; ones > 0; --bit)
    {
        uint64_t nCk = choose(bit, ones);
        if (ordinal >= nCk)
        {
            ordinal -= nCk;
            bits |= 1 << bit;
            --ones;
        }
    }
    return bits;
}

Lưu ý rằng vì bạn chỉ cần hệ số nhị thức lên đến 64, nên bạn có thể tính toán trước chúng.


  • Bìa, T., Mã hóa nguồn số . Giao dịch của IEEE về Lý thuyết thông tin, Tập IT-19, số 1, tháng 1 năm 1973.

Đẹp và thanh lịch! Mã hóa toàn diện trông giống như một cái gì đó rất hữu ích - có tài nguyên tốt nào trên đó không (tốt nhất là ở dạng sách giáo khoa)?
Koz Ross

Điều này thực sự cho hiệu suất tốt hơn trong thực tế? (Tất nhiên nó phụ thuộc vào tốc độ của RNG.) Nếu không thì không có điểm nào trong việc sử dụng mã phức tạp hơn.
Gilles 'SO- ngừng trở nên xấu xa'

1
@Giles Tôi giải thích đây là một câu hỏi về khoa học máy tính, vì đây là cs.se. Tôi chỉ đưa ra mã nguồn bởi vì tôi tình cờ thấy nó nằm xung quanh từ việc triển khai mảng RRR. (Xem, ví dụ, alexbowe.com/rrr để được giải thích về điều đó có nghĩa là gì.)
Bút danh

1
@Gilles Để theo dõi câu hỏi của bạn, tôi đã triển khai cả phương pháp ngây thơ của mình và phương pháp do Pseudonymous cung cấp trong Forth. Phương thức ngây thơ, ngay cả khi sử dụng PRNG xorshift rất đơn giản, đã lấy thứ gì đó theo thứ tự 20 giây mỗi số , trong khi phương thức của Pseudonymous gần như tức thời. Tôi đã sử dụng các bảng nhị thức được tính toán trước cho việc này.
Koz Ross

1
@KozRoss Nếu bạn tạo số n bit và tìm số có k bit được đặt, chúng sẽ khá hiếm nếu k cách xa n / 2; Điều đó sẽ giải thích nó.
gnasher729

3

Rất giống với câu trả lời của bút danh, có được bằng các phương tiện khác.

Tổng số kết hợp có sẵn có thể tiếp cận được bằng phương pháp sao và thanh , do đó, nó sẽ phải là . Tổng số các số 64 bit mà bạn sẽ cố lấy mẫu số của mình rõ ràng sẽ cao hơn nhiều so với số đó.c= =(64n)

Những gì bạn cần sau đó là một hàm có thể dẫn bạn từ số giả ngẫu , từ đến , đến tổ hợp 64 bit tương ứng.1 ck1c

Tam giác của Pascal có thể giúp bạn điều đó, bởi vì mọi giá trị của nút thể hiện chính xác số lượng đường dẫn từ nút đó đến gốc của tam giác và mọi đường dẫn có thể được thực hiện để đại diện cho một trong các chuỗi bạn đang tìm kiếm, nếu tất cả các rẽ trái là được gắn nhãn và mỗi lượt rẽ phải có .010

Vì vậy, gọi là số bit còn lại để xác định và là số lượng bit còn lại để sử dụng.yxy

Chúng tôi biết rằng và chúng tôi có thể sử dụng nó để xác định chính xác bit tiếp theo của số ở mỗi bước:(xy)= =(x-1y)+(x-1y-1)

whTôitôiex>0

Tôifx>y

ifk>(x1y):ss+"1",kk(x1y),yy1

else:ss+"0"

else:ss+"1",yy1

xx1


2

Một phương pháp khá thanh lịch khác là sử dụng phép chia đôi như được mô tả trong câu trả lời stackoverflow này . Ý tưởng là giữ hai từ, một từ được biết có tối đa k bit được đặt và một từ được biết có ít nhất k bit được đặt và sử dụng tính ngẫu nhiên để di chuyển một trong số đó theo hướng có chính xác k bit. Đây là một số mã nguồn để minh họa nó:

word randomKBits(int k) {
    word min = 0;
    word max = word(~word(0)); // all 1s
    int n = 0;
    while (n != k) {
        word x = randomWord();
        x = min | (x & max);
        n = popcount(x);
        if (n > k)
            max = x;
        else
            min = x;
    }
    return min;
}

Tôi đã thực hiện một so sánh hiệu suất của các phương pháp khác nhauphương pháp này thường là nhanh nhất trừ khi k được biết là rất nhỏ.


0

Bạn có thể làm như sau:

k164

k01

n

A[]640

for(i=1 to n)
{
    k=ran(1,65-i) % random number between 1 and 65-i
    for(x=1;x<65;x++)
    {
        if(A[x]==0)k--;
        if(k==0)break;
    }
    A[x]=1;
}

Văn xuôi dường như không khớp với mã của bạn? Mã không bao giờ gán 1s cho mảng. Ngoài ra, nó dường như không tạo ra một phân phối đồng đều (và không phải là số chẵn thỏa mãn các ràng buộc) khi nhiều kva chạm
Bergi

Một[x]= =1Tôif(Một[x]==0)k--;

À, tôi hiểu rồi. Thuật toán văn xuôi không đề cập đến việc bỏ qua.
Bergi

@ArghyaChakraborty Bạn có đang sử dụng lập chỉ mục 1 dựa trên đó không?
Koz Ross

Tôi= =1,k= =1MộtMột[1]==0trbạnek--;k= =0Một[1]= =1for(x= =0;x<64;x++)
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.