PRNG สำหรับสร้างตัวเลขด้วย n บิตตั้งค่า


12

ฉันกำลังเขียนโค้ดบางส่วนเพื่อสร้างข้อมูลไบนารี ฉันต้องการสร้างหมายเลข 64 บิตด้วยจำนวนบิตที่กำหนดโดยเฉพาะ แม่นยำยิ่งขึ้นขั้นตอนควรใช้เวลาและส่งกลับตัวเลข 64 บิตหลอกเทียมด้วยบิตตั้งค่าเป็นและส่วนที่เหลือตั้งค่าเป็น 00<n<64n1

แนวทางปัจจุบันของฉันเกี่ยวข้องกับสิ่งนี้:

  1. สร้าง pseudorandom 64 บิตจำนวนkk
  2. นับบิตในจัดเก็บผลในขkb
  3. ถ้า , เอาต์พุต ; มิฉะนั้นไปที่ 1b=nk

ใช้งานได้ แต่ดูเหมือนไม่เหมาะสม มีอัลกอริธึม PRNG บางประเภทที่สามารถสร้างตัวเลขด้วยบิตเซตยิ่งกว่านี้หรือไม่?n

คำตอบ:


12

สิ่งที่คุณต้องเป็นตัวเลขสุ่มระหว่าง 0 และ 1 ปัญหาก็คือการทำให้สิ่งนี้เป็นรูปแบบบิต(64n)1

สิ่งนี้เรียกว่าการเข้ารหัสแบบนับจำนวนและเป็นหนึ่งในอัลกอริธึมการบีบอัดที่เก่าแก่ที่สุดที่ปรับใช้ อาจเป็นอัลกอริทึมที่ง่ายที่สุดมาจาก Thomas Cover มันขึ้นอยู่กับการสังเกตอย่างง่าย ๆ ว่าถ้าคุณมีคำที่ยาวบิตโดยที่บิตที่ตั้งไว้คือตามลำดับบิตที่สำคัญที่สุดจากนั้นตำแหน่งของคำนี้ในการเรียงลำดับพจนานุกรมของคำทั้งหมดที่มีคุณสมบัตินี้ คือ:nxkx1

1ik(xii)

ตัวอย่างเช่นสำหรับคำ 7 บิต:

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

... และต่อไป

เพื่อให้ได้รูปแบบบิตจากลำดับคุณเพียงถอดรหัสแต่ละบิตในทางกลับกัน บางสิ่งเช่นนี้ในภาษาที่เหมือน 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;
}

โปรดทราบว่าเนื่องจากคุณต้องการค่าสัมประสิทธิ์ทวินามถึง 64 คุณจึงสามารถคำนวณล่วงหน้าได้


  • หน้าปก, T. , การเข้ารหัสแหล่งที่มาแบบนับจำนวน ทฤษฎีการทำธุรกรรมเกี่ยวกับข้อมูลของ IEEE, Vol IT-19, No 1, Jan 1973

สวยและสง่างาม! การเข้ารหัสแบบนับจำนวนดูเหมือนว่าสิ่งที่มีประโยชน์มาก - มีแหล่งข้อมูลที่ดีอยู่หรือไม่ (ควรอยู่ในรูปแบบตำราเรียน)?
Koz Ross

สิ่งนี้ให้ผลการปฏิบัติที่ดีขึ้นจริงหรือไม่? (แน่นอนว่ามันขึ้นอยู่กับความเร็วของ RNG) ถ้าไม่เช่นนั้นจะไม่มีประโยชน์ในการใช้รหัสที่ซับซ้อนมากขึ้น
Gilles 'หยุดความชั่วร้าย'

1
@Giles ฉันตีความว่านี่เป็นคำถามวิทยาศาสตร์คอมพิวเตอร์เนื่องจากนี่คือ cs.se ฉันแค่ให้ซอร์สโค้ดเพราะฉันได้วางมันไว้รอบ ๆ จากการใช้งานอาร์เรย์ RRR (ดูตัวอย่างalexbowe.com/rrrสำหรับคำอธิบายความหมาย)
นามแฝง

1
@Gilles เพื่อติดตามคำถามของคุณฉันใช้ทั้งวิธีการไร้เดียงสาของฉันและวิธีการหลอกโดย Fseudonym ใน Forth วิธีการที่ไร้เดียงสาแม้ใช้ xorshift PRNG ที่เรียบง่ายมากใช้เวลา 20 วินาทีต่อหมายเลขในขณะที่วิธี Pseudonym เกือบจะทันที ฉันใช้ตารางชื่อทวินามที่คำนวณล่วงหน้าสำหรับสิ่งนี้
Koz Ross

1
@ KozRoss หากคุณสร้างตัวเลข n บิตและค้นหาตัวเลขด้วยการตั้งค่าบิต k พวกเขาจะค่อนข้างหายากถ้า k อยู่ห่างจาก n / 2; ที่จะอธิบายมัน
gnasher729

3

คล้ายกับคำตอบของนามแฝงที่ได้รับจากวิธีการอื่น

จำนวนรวมของการอยู่รวมกันพร้อมใช้งานเป็นเรื่องง่ายโดยดาวและบาร์วิธีจึงจะต้องมีการ{n} จำนวนรวมของตัวเลข 64- บิตที่คุณจะลองตัวอย่างตัวเลขของคุณจะสูงกว่านั้นอย่างเห็นได้ชัดc=(64n)

สิ่งที่คุณต้องการแล้วเป็นฟังก์ชันที่สามารถนำคุณจากหมายเลข pseudorandomตั้งแต่เพื่อเพื่อการรวมกัน 64 บิตที่สอดคล้องกันk1c

สามเหลี่ยมของปาสคาลสามารถช่วยคุณได้เพราะค่าของทุกโหนดแสดงถึงจำนวนเส้นทางที่แน่นอนจากโหนดนั้นไปยังรูทของสามเหลี่ยมและทุกเส้นทางสามารถทำเพื่อเป็นตัวแทนของหนึ่งในสตริงที่คุณกำลังมองหาหากเลี้ยวซ้ายทั้งหมด ที่มีป้ายกำกับด้วยและทุกเลี้ยวขวาด้วย010

ดังนั้นให้เป็นจำนวนบิตที่เหลือเพื่อตรวจสอบและคือจำนวนของบิตที่เหลือเพื่อใช้xy

เรารู้ว่าและเราสามารถใช้มันเพื่อกำหนดจำนวนบิตถัดไปอย่างถูกต้อง ในแต่ละขั้นตอน:(xy)=(x1y)+(x1y1)

whilex>0

ifx>y

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

else:ss+"0"

else:ss+"1",yy1

xx1


2

อีกวิธีที่ค่อนข้างหรูหราคือการใช้ bisection ตามที่อธิบายไว้ในคำตอบ stackoverflowนี้ ความคิดคือการเก็บสองคำหนึ่งรู้ว่ามี k บิตมากที่สุดและเป็นที่รู้กันว่ามีอย่างน้อย k บิตตั้งและใช้การสุ่มเพื่อย้ายหนึ่งเหล่านี้ไปสู่การมี k บิตแน่นอน นี่คือซอร์สโค้ดบางส่วนที่จะแสดง:

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

ฉันทำการเปรียบเทียบประสิทธิภาพของวิธีการต่างๆและวิธีนี้มักจะเร็วที่สุดยกเว้นว่า k นั้นมีขนาดเล็กมาก


0

คุณสามารถทำสิ่งต่อไปนี้:

1) สร้างจำนวนสุ่มระหว่างและ64k164

2) การตั้งค่า THที่จะ1k01

3) ทำซ้ำขั้นตอนที่ 1 และ 2ครั้ง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;
}

ดูเหมือนจะเป็นร้อยแก้วที่ไม่ตรงกับรหัสของคุณ? รหัสไม่เคยกำหนด1s ให้กับอาร์เรย์ นอกจากนี้ดูเหมือนว่าจะไม่สร้างการแจกแจงแบบสม่ำเสมอ (และไม่ใช่แม้แต่ตัวเลขที่ตรงตามข้อ จำกัด ) เมื่อการkชนหลายครั้งเกิดขึ้น
Bergi

@Bergi Ya ลืมบรรทัด ... เพิ่มแล้วตอนนี้ และจัดการการชนกันของ k หลายอย่าง ดูหมายเลขแรกถูกเลือกระหว่าง 1 ถึง 64, ที่สองระหว่าง 1 และ "เหลือ" 63 ดังนั้นมันจะข้าม 1 ในขณะที่นับ ... ดูไลน์. และมันก็คือการกระจายตัวที่สม่ำเสมอ A[x]=1if(A[x]==0)k;
ผู้ใช้ไม่พบ

อ่าฉันเห็นแล้ว อัลกอริธึมร้อยแก้วไม่ได้กล่าวถึงการกระโดดข้าม
Bergi

@ArghyaChakraborty คุณกำลังใช้การจัดทำดัชนีแบบ 1 ฐานที่นั่นหรือไม่?
Koz Ross

@ KozRoss เริ่มต้นด้วยสิ่งที่เกิดขึ้นถ้า (แน่นอนว่าจะเป็นศูนย์ทั้งหมด) ดังนั้นมันจะตรวจสอบและรับหมายที่ซึ่งจะช่วยให้ 0 ดังนั้นตั้งนอกวง ใช่แล้วมันคือการจัดทำดัชนีแบบ 1 ในการทำให้เป็น 0 ทั้งหมดที่คุณต้องทำคือเปลี่ยนค่าภายในเป็นi=1,k=1AA[1]==0truek;k=0A[1]=1for(x=0;x<64;x++)
ผู้ใช้ไม่พบ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.