การเลือกการเรียงลำดับแบบสุ่มต้องสุ่มมากขึ้นและน้อยกว่าคำถามของคุณ ให้ฉันอธิบาย
ข่าวร้าย: ต้องการการสุ่มมากขึ้น
ข้อบกพร่องพื้นฐานในแนวทางของคุณคือพยายามเลือกระหว่าง ~ 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 :)
Random
ที่ไม่เคยจริงตัวเลขสุ่ม มันเป็น PRNG ที่ P ย่อมาจาก "pseudo" สำหรับตัวเลขสุ่มจริงคุณต้องมีแหล่งที่มาของการสุ่ม (เช่น random.org)