java.util.Random เป็นแบบสุ่มจริงๆเหรอ? ฉันจะสร้าง 52 ได้อย่างไร! (แฟกทอเรียล) ลำดับที่เป็นไปได้?


202

ฉันใช้Random (java.util.Random)เพื่อสับไพ่ 52 ใบ มี 52! (8.0658175e + 67) ความเป็นไปได้ แต่ฉันได้พบว่าเมล็ดพันธุ์แห่งjava.util.Randomนี้คือ a longซึ่งเล็กกว่ามากที่ 2 ^ 64 (1.8446744e + 19)

จากที่นี่ฉันสงสัยว่าjava.util.Random มันสุ่มจริงๆหรือเปล่า จริง ๆ แล้วมันสามารถสร้างทั้ง 52! ความเป็นไปได้?

ถ้าไม่ฉันจะสร้างซีเควนซ์สุ่มที่ดีกว่าที่สามารถผลิตทั้ง 52 อย่างน่าเชื่อถือ ความเป็นไปได้?


21
"ฉันจะสร้างเลขสุ่มจริง ๆ ได้มากกว่า 52 ได้อย่างไร" ตัวเลขจากRandomที่ไม่เคยจริงตัวเลขสุ่ม มันเป็น PRNG ที่ P ย่อมาจาก "pseudo" สำหรับตัวเลขสุ่มจริงคุณต้องมีแหล่งที่มาของการสุ่ม (เช่น random.org)
TJ Crowder

7
@JimGarrison นั่นไม่ใช่สิ่งที่ OP จะดำเนินการ เขากำลังพูดถึงลำดับที่เป็นไปได้ 10 ^ 68 เนื่องจากแต่ละลำดับของการสุ่มหลอกถูกระบุโดยเมล็ดของมัน OP กล่าวว่าอาจมีลำดับที่แตกต่างกันมากที่สุด 2 ^ 64
dasblinkenlight

6
ฉันคิดว่ามันเป็นคำถามที่น่าสนใจและคุ้มค่าที่จะคิด แต่ฉันไม่สามารถช่วยสงสัยเกี่ยวกับบริบทของปัญหาของคุณ: อะไรคือสิ่งที่นำไปสู่ความต้องการที่จะสร้างทั้งหมด 52! พีชคณิต? ตัวอย่างเช่นในสะพานแห่งความเป็นจริงเราสามารถสับไพ่และแจกไพ่ครั้งละใบ แต่มีมือที่แตกต่างกันเพียง ~ 6e11 เนื่องจากการเรียงสับเปลี่ยนที่แตกต่างกันส่งผลให้อยู่ในมือเดียวกัน เมื่อคิดในอีกด้านหนึ่งคุณต้องการวิธีแก้ปัญหาที่เฉพาะเจาะจงสำหรับ 52! หรือคุณต้องการสิ่งที่พูดแบบทั่วไปพูดสองชั้นสับด้วยกัน (104! / (2 ** 52) ความเป็นไปได้หรือ ~ 2e150)?
NPE

9
@NPE - Take Solitaire (Klondike) เช่น 52! เป็นจำนวนมือที่เป็นไปได้อย่างแน่นอน ..
Serj Ardovic

3
ฉันคิดว่านี่เป็นการอ่านที่น่าสนใจ: superuser.com/a/712583
Dennis_E

คำตอบ:


153

การเลือกการเรียงลำดับแบบสุ่มต้องสุ่มมากขึ้นและน้อยกว่าคำถามของคุณ ให้ฉันอธิบาย

ข่าวร้าย: ต้องการการสุ่มมากขึ้น

ข้อบกพร่องพื้นฐานในแนวทางของคุณคือพยายามเลือกระหว่าง ~ 2 226ความเป็นไปได้โดยใช้ 64 บิตของเอนโทรปี (เมล็ดสุ่ม) ในการเลือกระหว่างความเป็นไปได้ของ~ 2 226คุณจะต้องหาวิธีในการสร้างเอนโทรปี 226 บิตแทนที่จะเป็น 64

มีหลายวิธีที่จะสร้างบิตสุ่มฮาร์ดแวร์เฉพาะ , คำแนะนำ CPU , อินเตอร์เฟซ OS , บริการออนไลน์ มีข้อสันนิษฐานโดยนัยอยู่ในคำถามของคุณว่าคุณสามารถสร้าง 64 บิตได้ดังนั้นเพียงแค่ทำสิ่งที่คุณกำลังจะทำเพียงสี่ครั้งและบริจาคบิตส่วนเกินเพื่อการกุศล :)

ข่าวดี: ต้องการความสุ่มน้อย

เมื่อคุณมีผู้ที่ 226 บิตสุ่มส่วนที่เหลือสามารถทำได้ deterministically และอื่น ๆคุณสมบัติของjava.util.Randomสามารถทำที่ไม่เกี่ยวข้อง นี่คือวิธี

สมมติว่าเราสร้างทั้งหมด 52! พีชคณิต (ทนกับฉัน) และเรียงลำดับพจนานุกรม

เลือกหนึ่งในพีชคณิตทั้งหมดที่เราต้องเป็นจำนวนเต็มสุ่มเดียวระหว่างและ0 52!-1จำนวนเต็มนั่นคือ 226 บิตของเอนโทรปีของเรา เราจะใช้มันเป็นดัชนีในรายการเรียงลำดับพีชคณิตของเรา หากดัชนีสุ่มกระจายอย่างสม่ำเสมอไม่เพียง แต่คุณรับรองว่าพีชคณิตทั้งหมดสามารถเลือกพวกเขาจะได้รับเลือกequiprobably (ซึ่งก็คือการรับประกันที่แข็งแกร่งกว่าสิ่งที่เป็นคำถามที่ถาม)

ตอนนี้คุณไม่จำเป็นต้องสร้างพีชคณิตทั้งหมด คุณสามารถสร้างหนึ่งโดยตรงรับตำแหน่งที่เลือกแบบสุ่มในรายการเรียงลำดับตามสมมติฐานของเรา ซึ่งสามารถทำได้ใน O (n 2 ) เวลาใช้Lehmer [1]รหัส (ดูเลขพีชคณิตและระบบเลข factoriadic ) n นี่คือขนาดของเด็คของคุณเช่น 52

มีการใช้งาน C ในคำตอบ StackOverflowนี้ มีตัวแปรจำนวนเต็มหลายตัวที่จะล้นสำหรับ n = 52 แต่โชคดีใน Java ที่คุณสามารถjava.math.BigIntegerใช้ได้ ส่วนที่เหลือของการคำนวณสามารถถอดความได้เกือบจะเป็น:

public static int[] shuffle(int n, BigInteger random_index) {
    int[] perm = new int[n];
    BigInteger[] fact = new BigInteger[n];
    fact[0] = BigInteger.ONE;
    for (int k = 1; k < n; ++k) {
        fact[k] = fact[k - 1].multiply(BigInteger.valueOf(k));
    }

    // compute factorial code
    for (int k = 0; k < n; ++k) {
        BigInteger[] divmod = random_index.divideAndRemainder(fact[n - 1 - k]);
        perm[k] = divmod[0].intValue();
        random_index = divmod[1];
    }

    // readjust values to obtain the permutation
    // start from the end and check if preceding values are lower
    for (int k = n - 1; k > 0; --k) {
        for (int j = k - 1; j >= 0; --j) {
            if (perm[j] <= perm[k]) {
                perm[k]++;
            }
        }
    }

    return perm;
}

public static void main (String[] args) {
    System.out.printf("%s\n", Arrays.toString(
        shuffle(52, new BigInteger(
            "7890123456789012345678901234567890123456789012345678901234567890"))));
}

[1]เพื่อไม่ให้สับสนกับLehrer :)


7
Heh และผมแน่ใจว่าการเชื่อมโยงในตอนท้ายจะเป็นใหม่คณิตศาสตร์ :-)
TJ Crowder

5
@TJCrowder: มันเกือบจะเป็น! มันเป็นแมนิโฟลด์ที่แตกต่างกันอย่างมากมายจนทำให้มันเปลี่ยนไป :-)
NPE

2
ดีที่ได้เห็นคนชื่นชมความคลาสสิก :-)
TJ Crowder

3
คุณได้รับ 226 บิตสุ่มในJavaที่ไหน ขออภัยรหัสของคุณไม่ตอบรับ
Thorsten S.

5
ฉันไม่เข้าใจว่าคุณหมายถึงอะไร Java Random () จะไม่ให้เอนโทรปี 64 บิตเช่นกัน OP หมายถึงซอร์สที่ไม่ระบุซึ่งสามารถสร้าง 64 บิตเพื่อ seed PRNG มันสมเหตุสมผลที่จะสมมติว่าคุณสามารถถามแหล่งเดียวกันสำหรับ 226 บิต
หยุดทำร้ายโมนิก้า

60

การวิเคราะห์ของคุณถูกต้อง: การเพาะเครื่องกำเนิดไฟฟ้าจำนวนสุ่มหลอกกับเมล็ดพันธุ์ที่เฉพาะเจาะจงใด ๆ จะต้องให้ผลผลิตลำดับเดียวกันหลังจากสับ จำกัด จำนวนของพีชคณิตที่คุณจะได้รับ 2 64 การยืนยันนี้ง่ายต่อการตรวจสอบการทดลองโดยการเรียกCollection.shuffleสองครั้งผ่านRandomวัตถุที่เริ่มต้นด้วยเมล็ดเดียวกันและสังเกตว่าการสับสุ่มสองอันนั้นเหมือนกัน

วิธีแก้ปัญหานี้คือการใช้ตัวสร้างตัวเลขสุ่มที่ช่วยให้เมล็ดที่มีขนาดใหญ่ขึ้น Java จัดเตรียมSecureRandomคลาสที่สามารถเริ่มต้นได้ด้วยbyte[]อาร์เรย์ที่มีขนาดไม่ จำกัด อย่างแท้จริง จากนั้นคุณสามารถส่งตัวอย่างของการSecureRandomให้ทำงานCollections.shuffleให้เสร็จสมบูรณ์:

byte seed[] = new byte[...];
Random rnd = new SecureRandom(seed);
Collections.shuffle(deck, rnd);

8
แน่นอนเมล็ดใหญ่ไม่ได้รับประกันว่าทั้งหมด 52! ความเป็นไปได้จะเกิดขึ้น (ซึ่งคำถามนี้เกี่ยวกับอะไรเป็นพิเศษ) ในการทดลองทางความคิดให้พิจารณา PRNG ทางพยาธิวิทยาที่ใช้เมล็ดพันธุ์ที่มีขนาดใหญ่โดยพลการ ดูเหมือนว่าค่อนข้างชัดเจนว่า PRNG ต้องการตอบสนองความต้องการมากกว่าการรับเมล็ดที่มีขนาดใหญ่พอ
NPE

2
@SerjArdovic ใช่วัสดุเมล็ดใด ๆ ที่ส่งผ่านไปยังวัตถุ SecureRandom ต้องไม่สามารถคาดเดาได้ตามเอกสาร Java
dasblinkenlight

10
@NPE คุณถูกต้องถึงแม้ว่าเมล็ดที่มีขนาดเล็กเกินไปคือการรับประกันวงเงินสูงสุด แต่เมล็ดที่มีขนาดใหญ่พอจะไม่รับประกันในวงเงินที่ต่ำกว่า ทั้งหมดนี้คือการยกเลิกขีด จำกัด บนทางทฤษฎีทำให้ RNG สามารถสร้างทั้งหมด 52! อยู่รวมกัน
dasblinkenlight

5
@SerjArdovic จำนวนไบต์ที่เล็กที่สุดที่ต้องการคือ 29 (คุณต้องการ 226 บิตเพื่อเป็นตัวแทน 52 บิตที่เป็นไปได้รวมกันซึ่งเป็น 28.25 ไบต์ดังนั้นเราต้องปัดเศษขึ้น) โปรดทราบว่าการใช้วัสดุเมล็ด 29 ไบต์ลบขีด จำกัด บนเชิงทฤษฎีเกี่ยวกับจำนวนของสับที่คุณจะได้รับโดยไม่ต้องสร้างขีด จำกัด ล่าง (ดูความคิดเห็นของ NPE เกี่ยวกับ RNG เส็งเคร็งที่ใช้เมล็ดขนาดใหญ่มากและสร้างลำดับของศูนย์ทั้งหมด)
dasblinkenlight

8
การSecureRandomติดตั้งจะใช้ PRNG พื้นฐานอย่างแน่นอน และขึ้นอยู่กับช่วงเวลาของ PRNG (และในระดับที่น้อยกว่า, ความยาวของรัฐ) ไม่ว่าจะสามารถเลือกได้จากการเปลี่ยนลำดับของปัจจัยทั้ง 52 (โปรดทราบว่าเอกสารประกอบกล่าวว่าการSecureRandomดำเนินการ "น้อยที่สุดสอดคล้องกับ" การทดสอบทางสถิติบางอย่างและสร้างผลลัพธ์ที่ "ต้องมีความแข็งแกร่งของการเข้ารหัส" แต่ไม่ จำกัด ขีด จำกัด ล่างที่ชัดเจนเกี่ยวกับความยาวของรัฐ PRNG พื้นฐานหรือตามระยะเวลา)
Peter O.

26

โดยทั่วไปตัวสร้างตัวเลขปลอม (PRNG) ไม่สามารถเลือกจากรายการเรียงลำดับทั้งหมด 52 รายการหากสถานะความยาวน้อยกว่า 226 บิต

java.util.Randomใช้อัลกอริทึมกับโมดูลัสของ 2 48 ; ดังนั้นความยาวสถานะของมันคือ 48 บิตดังนั้นน้อยกว่า 226 บิตที่ฉันอ้างถึง คุณจะต้องใช้ PRNG อื่นที่มีความยาวของรัฐที่ใหญ่กว่า - โดยเฉพาะหนึ่งอันที่มีระยะเวลาเป็น 52 แฟคทอเรียลหรือสูงกว่า

ดูเพิ่มเติม "สับ" ฉันในบทความเกี่ยวกับเครื่องกำเนิดไฟฟ้าจำนวนสุ่ม

การพิจารณานี้เป็นอิสระจากลักษณะของ PRNG มันใช้อย่างเท่าเทียมกันกับ PRNG ที่เข้ารหัสและไม่เข้ารหัส (แน่นอนว่า PRNG ที่ไม่ใช่การเข้ารหัสนั้นไม่เหมาะสมเมื่อใดก็ตามที่เกี่ยวข้องกับความปลอดภัยของข้อมูล)


แม้ว่าจะjava.security.SecureRandomอนุญาตให้เมล็ดที่มีความยาวไม่ จำกัด สามารถส่งผ่านได้ แต่การSecureRandomใช้งานอาจใช้ PRNG พื้นฐาน (เช่น "SHA1PRNG" หรือ "DRBG") และขึ้นอยู่กับช่วงเวลาของ PRNG (และในระดับที่น้อยกว่า, ความยาวของรัฐ) ไม่ว่าจะสามารถเลือกได้จากการเปลี่ยนลำดับของปัจจัยทั้ง 52 (โปรดทราบว่าฉันกำหนด "state state"เป็น "ขนาดสูงสุดของเมล็ดที่ PRNG สามารถใช้เพื่อเริ่มต้นสถานะโดยไม่ต้องย่อหรือบีบอัดเมล็ดนั้น ")


18

ให้ฉันขอโทษล่วงหน้าเพราะนี่เป็นเรื่องยากที่จะเข้าใจ ...

ก่อนอื่นคุณรู้อยู่แล้วว่าjava.util.Randomมันไม่ได้สุ่มอย่างสมบูรณ์เลย มันสร้างลำดับในวิธีที่คาดเดาได้อย่างสมบูรณ์แบบจากเมล็ด คุณถูกต้องสมบูรณ์เนื่องจากเมล็ดมีความยาว 64 บิตเท่านั้นจึงสามารถสร้างลำดับที่ต่างกัน 2 ^ 64 เท่านั้น หากคุณกำลังจะสร้างอย่างใด 64 บิตสุ่มจริงและใช้พวกเขาเพื่อเลือกเมล็ดที่คุณไม่สามารถใช้เมล็ดพันธุ์ที่จะสุ่มเลือกระหว่างทั้งหมดของ 52! ลำดับที่เป็นไปได้ที่มีความน่าจะเท่ากัน

อย่างไรก็ตามความจริงนี้ไม่มีผลตราบใดที่คุณไม่ได้สร้างมากกว่า 2 ^ 64 ลำดับตราบใดที่ไม่มี 'พิเศษ' หรือ 'พิเศษอย่างเห็นได้ชัด' เกี่ยวกับลำดับ 2 ^ 64 ที่สามารถสร้างได้ .

ให้บอกว่าคุณมี PRNG ที่ดีกว่ามากที่ใช้เมล็ด 1,000 บิต ลองนึกภาพคุณมีสองวิธีในการเริ่มต้น - วิธีหนึ่งจะเริ่มต้นได้โดยใช้เมล็ดทั้งหมดและวิธีหนึ่งจะแฮชเมล็ดลงไปที่ 64 บิตก่อนที่จะเริ่มต้น

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

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

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

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

แก้ไข:

ฉันควรจะเพิ่มสิ่งนั้นแม้ว่าทั้งหมดข้างต้นถูกต้องว่าการใช้งานจริงของjava.util.Randomไม่น่ากลัว หากคุณกำลังเขียนเกมการ์ดอาจใช้MessageDigestAPI เพื่อสร้างแฮช SHA-256 "MyGameName"+System.currentTimeMillis()และใช้บิตเหล่านั้นเพื่อสลับเด็ค ตามอาร์กิวเมนต์ข้างต้นตราบใดที่ผู้ใช้ของคุณไม่ได้เล่นการพนันคุณไม่ต้องกังวลว่าcurrentTimeMillisจะได้รับผลตอบแทนที่ยาวนาน หากผู้ใช้ของคุณจะเล่นการพนันจริงๆแล้วใช้SecureRandomกับเมล็ดไม่มี


6
@ThorstenS คุณจะเขียนแบบทดสอบใด ๆ ที่ระบุได้ว่ามีการรวมกันของการ์ดที่ไม่สามารถเกิดขึ้นได้อย่างไร
Matt Timmermans

2
มีห้องทดสอบตัวเลขสุ่มหลายชุดเช่น Diehard จาก George Marsaglia หรือ TestU01 จาก Pierre L'Ecuyer / Richard Simard ซึ่งสามารถค้นหาความผิดปกติทางสถิติได้อย่างง่ายดายในเอาต์พุตแบบสุ่ม สำหรับการตรวจสอบการ์ดคุณสามารถใช้สองช่อง คุณกำหนดใบสั่งบัตร สี่เหลี่ยมจัตุรัสแรกแสดงตำแหน่งของไพ่สองใบแรกเป็นคู่ xy: ไพ่ใบแรกเป็น x และตำแหน่งที่แตกต่าง (!) (-26-25) ของไพ่ใบที่สองเป็น y สี่เหลี่ยมจัตุรัสที่สองแสดงไพ่ใบที่ 3 และ 4 ที่มี (-25-25) ที่สัมพันธ์กับ 2nd / 3rd สิ่งนี้จะแสดงช่องว่างและกลุ่มในการแจกจ่ายของคุณทันทีหากคุณเรียกใช้เป็นระยะเวลาหนึ่ง
Thorsten S.

4
นั่นไม่ใช่การทดสอบที่คุณบอกว่าคุณสามารถเขียนได้ แต่มันก็ใช้ไม่ได้เช่นกัน ทำไมคุณคิดว่ามีช่องว่างและกลุ่มในการแจกจ่ายที่การทดสอบดังกล่าวจะเปิดเผย? นั่นหมายถึง "ความอ่อนแอที่เฉพาะเจาะจงในการดำเนินการ PRNG" ตามที่ฉันกล่าวถึงและไม่มีอะไรเกี่ยวข้องกับจำนวนเมล็ดพันธุ์ที่เป็นไปได้ การทดสอบดังกล่าวไม่ได้ต้องการให้คุณส่งเครื่องกำเนิดซ้ำ ฉันเตือนเมื่อเริ่มต้นว่านี่ยากที่จะเข้าใจ
Matt Timmermans

3
@ThorstenS ชุดทดสอบเหล่านั้นจะไม่ตัดสินว่าแหล่งที่มาของคุณเป็น PRNG แบบเข้ารหัสที่มีความปลอดภัย 64 บิตหรือไม่หรือ RNG ที่แท้จริง (การทดสอบ PRNG คือสิ่งที่ชุดเหล่านั้นมีให้หลังจากทั้งหมด) แม้ว่าคุณจะรู้วิธีการใช้งาน แต่ PRNG ที่ดีทำให้ไม่สามารถระบุรัฐได้โดยไม่ต้องค้นหาพื้นที่รัฐ
Sneftel

1
@ThorstenS: ในไพ่สำรับที่แท้จริงชุดค่าผสมส่วนใหญ่จะไม่เกิดขึ้น คุณเพียงแค่ไม่รู้ว่าคนเหล่านั้นเป็นใคร สำหรับ PRNG ที่เหมาะสมครึ่งหนึ่งจะเหมือนกัน - หากคุณสามารถทดสอบว่าลำดับเอาต์พุตที่กำหนดที่มีความยาวอยู่ในภาพหรือไม่นั่นเป็นข้อบกพร่องของ PRNG รัฐ / ช่วงเวลาที่ยิ่งใหญ่อย่างน่าขันเช่น 52! ไม่จำเป็น 128 บิตควรเพียงพอ
. GitHub หยุดช่วยน้ำแข็ง

10

ฉันจะใช้วิธีที่แตกต่างกันเล็กน้อยเกี่ยวกับเรื่องนี้ คุณคิดถูกแล้ว - PRNG ของคุณจะไม่สามารถโจมตี 52 ทั้งหมดได้! ความเป็นไปได้

คำถามคือสิ่งที่ขนาดของการ์ดเกมของคุณ?

หากคุณกำลังสร้างเกมสไตล์ klondike ง่ายๆ? ถ้าอย่างนั้นคุณไม่จำเป็นต้องใช้ 52 ทั้งหมด! ความเป็นไปได้ แต่มองไปที่มันเช่นนี้ผู้เล่นจะมี 18 Quintillionเกมที่แตกต่างกัน แม้แต่การบัญชีสำหรับ 'ปัญหาวันเกิด' พวกเขาต้องการเล่นหลายพันล้านมือก่อนที่จะพบกับเกมที่ซ้ำกันครั้งแรก

หากคุณกำลังจำลองสถานการณ์ monte-carlo แล้วคุณอาจจะไม่เป็นไร คุณอาจต้องจัดการกับสิ่งประดิษฐ์เนื่องจาก 'P' ใน PRNG แต่คุณอาจจะไม่เจอกับปัญหาเพียงเพราะพื้นที่เพาะเมล็ดเหลือน้อย (อีกครั้งคุณกำลังดูความเป็นไปได้ที่ไม่ซ้ำกัน) พลิกด้านถ้าคุณกำลังทำงานกับการนับซ้ำขนาดใหญ่แล้วใช่พื้นที่เมล็ดต่ำของคุณอาจเป็นตัวจัดการ

ถ้าคุณทำเกมการ์ดแบบผู้เล่นหลายคนโดยเฉพาะถ้ามีเงินอยู่ในสาย? ถ้าอย่างนั้นคุณจะต้องทำ googling ว่าเว็บไซต์โป๊กเกอร์ออนไลน์จัดการปัญหาเดียวกันกับที่คุณถามหรือไม่ เนื่องจากในขณะที่ปัญหาพื้นที่เมล็ดต่ำไม่ได้สังเกตเห็นโดยผู้เล่นโดยเฉลี่ยมันจะเป็นประโยชน์ถ้าคุ้มค่ากับการลงทุน (เว็บไซต์โป๊กเกอร์ทั้งหมดผ่านขั้นตอนที่ PRNG ของพวกเขาถูก 'แฮ็ก' ให้บางคนเห็นไพ่ในหลุมของผู้เล่นอื่นทั้งหมดเพียงแค่ลดหย่อนเมล็ดจากไพ่ที่เปิดเผย) หากนี่เป็นสถานการณ์ที่คุณอยู่อย่าสวมใส่ 'Tเพียงแค่หา PRNG ดีกว่า - คุณจะต้องรักษามันเป็นอย่างจริงจังเป็นปัญหาการเข้ารหัสลับ


9

วิธีแก้ปัญหาแบบสั้นซึ่งเป็นพื้นฐานของ dasblinkenlight:

// Java 7
SecureRandom random = new SecureRandom();
// Java 8
SecureRandom random = SecureRandom.getInstanceStrong();

Collections.shuffle(deck, random);

คุณไม่จำเป็นต้องกังวลเกี่ยวกับสถานะภายใน คำอธิบายที่ยาวทำไม:

เมื่อคุณสร้างSecureRandomอินสแตนซ์ด้วยวิธีนี้มันจะเข้าถึงตัวสร้างตัวเลขสุ่มเฉพาะระบบปฏิบัติการ นี่เป็นพูลเอนโทรปีที่มีการเข้าถึงค่าซึ่งมีบิตแบบสุ่ม (เช่นสำหรับตัวจับเวลานาโนวินาทีความแม่นยำระดับนาโนวินาทีคือการสุ่ม) หรือตัวสร้างหมายเลขฮาร์ดแวร์ภายใน

อินพุตนี้! นั่นคือเหตุผลที่ CSPRNG เหล่านั้นถูกใช้ไม่ใช่เพื่อสร้างตัวเลขเหล่านั้น! SecureRandomมีเคาน์เตอร์ที่มีร่องรอยว่าหลายบิตถูกนำมาใช้ ( getBytes(), getLong()ฯลฯ ) และเติมSecureRandomกับบิตเอนโทรปีเมื่อมีความจำเป็น

กล่าวโดยย่อ: เพียงลืมคำคัดค้านและใช้SecureRandomเป็นตัวสร้างตัวเลขสุ่มจริง


4

หากคุณพิจารณาตัวเลขเป็นเพียงอาร์เรย์ของบิต (หรือ bytes) แล้วบางทีคุณอาจจะใช้ (Secure) Random.nextBytesแก้ปัญหาในเรื่องนี้กองมากเกินnew BigInteger(byte[])คำถามและจากนั้นแมอาร์เรย์เข้าไปที่


3

อัลกอริทึมที่ง่ายมากคือการใช้ SHA-256 กับลำดับของจำนวนเต็มที่เพิ่มขึ้นจาก 0 ขึ้นไป (เกลือสามารถต่อท้ายได้ถ้าต้องการ "รับลำดับที่แตกต่างกัน") ถ้าเราสมมติว่าผลลัพธ์ของ SHA-256 คือ "ดีเท่ากับ" จำนวนเต็มกระจายอย่างสม่ำเสมอระหว่าง 0 ถึง 2 256 - 1 แล้วเรามีเอนโทรปีเพียงพอสำหรับ งาน.

ในการรับการเปลี่ยนแปลงจากผลลัพธ์ของ SHA256 (เมื่อแสดงเป็นจำนวนเต็ม) เพียงแค่ต้องการลด modulo 52, 51, 50 ... เช่นเดียวกับใน pseudocode นี้:

deck = [0..52]
shuffled = []
r = SHA256(i)

while deck.size > 0:
    pick = r % deck.size
    r = floor(r / deck.size)

    shuffled.append(deck[pick])
    delete deck[pick]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.