PRNG untuk menghasilkan angka dengan n mengatur bit dengan tepat

12

Saat ini saya sedang menulis beberapa kode untuk menghasilkan data biner. Saya secara khusus perlu menghasilkan angka 64-bit dengan jumlah bit yang ditentukan; lebih tepatnya, prosedur harus mengambil beberapa 0<n<64 dan mengembalikan nomor pseudo-acak 64-bit dengan tepat n bit diatur ke 1 , dan sisanya diatur ke 0.

Pendekatan saya saat ini melibatkan sesuatu seperti ini:

  1. Hasilkan angka pseudorandom 64-bit k .
  2. Hitung bit dalam k , menyimpan hasilnya dalam b .
  3. Jika b=n , output k ; kalau tidak pergi ke 1.

Ini berfungsi, tetapi tampaknya tidak elegan. Apakah ada semacam algoritma PRNG yang dapat menghasilkan angka dengan n menetapkan bit lebih elegan dari ini?

Koz Ross
sumber

Jawaban:

12

Yang Anda butuhkan adalah angka acak antara 0 dan . Masalahnya kemudian adalah mengubah ini menjadi pola bit.(64n)1

Ini dikenal sebagai enumerative coding, dan ini adalah salah satu algoritma kompresi yang digunakan tertua. Mungkin algoritma paling sederhana adalah dari Thomas Cover. Ini didasarkan pada pengamatan sederhana bahwa jika Anda memiliki kata yang panjangnya bit, di mana bit yang adalah dalam urutan bit paling signifikan, maka posisi kata ini dalam urutan leksikografis semua kata dengan properti ini adalah:x kx 1nxkx1

1ik(xii)

Jadi, misalnya, untuk kata 7-bit:

i(0001011)= ( 3

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

...dan seterusnya.

Untuk mendapatkan pola bit dari ordinal, Anda cukup mendekodekan setiap bit secara bergantian. Sesuatu seperti ini, dalam bahasa seperti-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;
}

Perhatikan bahwa karena Anda hanya membutuhkan koefisien binomial hingga 64, Anda dapat melakukan precompute.


  • Sampul, T., Pengkodean Sumber Enumeratif . Transaksi IEEE pada Teori Informasi, Vol IT-19, No 1, Jan 1973.
Nama samaran
sumber
Cantik dan elegan! Pengkodean enumeratif terlihat seperti sesuatu yang sangat berguna - adakah sumber daya yang bagus di sana (lebih disukai dalam bentuk buku teks)?
Koz Ross
Apakah ini benar-benar memberikan kinerja yang lebih baik dalam praktik? (Tentu saja itu tergantung pada kecepatan RNG.) Jika tidak maka tidak ada gunanya menggunakan kode yang lebih kompleks.
Gilles 'SANGAT berhenti menjadi jahat'
1
@ Giles Saya menafsirkan ini sebagai pertanyaan ilmu komputer, karena ini adalah cs.se. Saya hanya memberikan kode sumber karena kebetulan saya meletakkannya dari implementasi array RRR. (Lihat, misalnya, alexbowe.com/rrr untuk penjelasan tentang apa artinya itu.)
Nama samaran
1
@Gilles Untuk menindaklanjuti pertanyaan Anda, saya menerapkan metode naif saya dan yang disediakan oleh Pseudonim di Forth. Metode naif, bahkan ketika menggunakan PRNG xorshift yang sangat sederhana, mengambil sesuatu dalam urutan 20 detik per angka , sementara metode Pseudonim hampir instan. Saya menggunakan tabel binomial yang dikomputasi untuk ini.
Koz Ross
1
@KozRoss Jika Anda menghasilkan angka n bit, dan mencari angka dengan k bit yang diset, mereka akan sangat jarang jika k jauh dari n / 2; itu akan menjelaskannya.
gnasher729
3

Sangat mirip dengan jawaban Pseudonim, diperoleh dengan cara lain.

Jumlah total kombinasi yang tersedia dapat didekati dengan metode bintang dan balok , sehingga harus . Jumlah total angka 64-bit dari mana Anda akan mencoba untuk mengambil sampel nomor Anda akan jauh lebih tinggi dari itu.c=(64n)

Yang Anda butuhkan adalah fungsi yang dapat mengarahkan Anda dari angka pseudorandom , mulai dari hingga , hingga kombinasi 64-bit yang sesuai.1 ck1c

Segitiga Pascal dapat membantu Anda dengan hal itu, karena nilai setiap simpul mewakili jumlah persis jalur dari simpul tersebut ke akar segitiga, dan setiap jalur dapat dibuat untuk mewakili salah satu string yang Anda cari, jika semua belokan kiri adalah diberi label dengan , dan setiap belokan kanan dengan .010

Jadi, biarkan menjadi jumlah bit yang tersisa untuk ditentukan, dan menjadi jumlah yang tersisa untuk digunakan.yxy

Kita tahu bahwa , dan kita dapat menggunakannya untuk menentukan bit nomor berikutnya dengan benar di setiap langkah:(xy)=(x1y)+(x1y1)

whilex>0

ifx>y

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

else:ss+"0"

else:ss+"1",yy1

xx1

André Souza Lemos
sumber
2

Metode lain yang cukup elegan adalah dengan menggunakan pembagian dua seperti yang dijelaskan dalam jawaban stackoverflow ini . Idenya adalah untuk menjaga dua kata, satu diketahui memiliki paling banyak bit k set dan satu diketahui memiliki setidaknya bit k set, dan menggunakan keacakan untuk memindahkan salah satu dari ini ke arah memiliki bit k yang tepat. Berikut ini beberapa kode sumber untuk menggambarkannya:

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

Saya membuat perbandingan kinerja berbagai metode dan ini biasanya yang tercepat kecuali k diketahui sangat kecil.

Falk Hüffner
sumber
0

Anda dapat melakukan hal berikut:

1) Menghasilkan angka acak, antara dan .k164

2) Tetapkan ke ke .k01

3) Ulangi langkah 1 dan 2 kalin

A[] adalah array bit dengan semua dtk640

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;
}
Pengguna tidak ditemukan
sumber
Prosa sepertinya tidak cocok dengan kode Anda? Kode tidak pernah memberikan 1s ke array. Juga tampaknya tidak menghasilkan distribusi yang seragam (dan bahkan angka yang tidak memenuhi kendala) ketika beberapa ks bertabrakan
Bergi
@Bergi Ya lupa garis ... ditambahkan sekarang. Dan beberapa tabrakan k ditangani. Lihat angka pertama dipilih antara 1 dan 64, kedua antara 1 dan "tersisa" 63. Jadi ia melompati 1 sambil menghitung ... lihatbaris. Dan itu adalah distribusi yang seragam. A[x]=1if(A[x]==0)k;
Pengguna Tidak Ditemukan
Ah, saya mengerti sekarang. Algoritma prosa tidak menyebutkan skipping.
Bergi
@ArghyaChakraborty Apakah Anda menggunakan pengindeksan berbasis 1 di sana?
Koz Ross
@KozRoss Mulai dengan apa yang terjadi jika (tentu saja akan menjadi nol semua) Jadi, itu akan memeriksa dan mendapatkan artiyang menghasilkan . Jadi, set luar loop. Jadi ya itu pengindeksan berbasis 1. Untuk menjadikannya berdasarkan 0 yang harus Anda lakukan adalah mengubah bagian dalam keA A [ 1 ] = = 0 t r u e k - - ; k = 0 A [ 1 ] = 1 f o r ( x = 0 ; x < 64 ; x + + )i=1,k=1AA[1]==0truek;k=0A[1]=1for(x=0;x<64;x++)
Pengguna Tidak Ditemukan