Rastgele bir permütasyon seçmek, aynı anda sorunuzun ima ettiğinden daha fazla ve daha az rastgele olmalıdır. Açıklamama izin ver.
Kötü haber: daha fazla rastgelelik gerekir.
Yaklaşımınızdaki temel kusur, 64 bit entropi (rastgele tohum) kullanarak ~ 226 olasılık arasında seçim yapmaya çalışmasıdır . ~ 2222 olasılık arasında adil bir seçim yapmak için 64 yerine 226 bit entropi üretmenin bir yolunu bulmanız gerekecektir.
Rastgele bitler üretmenin birkaç yolu vardır: özel donanım , CPU talimatları , işletim sistemi arabirimleri , çevrimiçi hizmetler . Sorunuzda bir şekilde 64 bit oluşturabileceğiniz konusunda örtük bir varsayım var, bu yüzden ne yapacaksanız yapın, sadece dört kez yapın ve fazla bitleri sadaka bağışlayın. :)
İyi haber: daha az rastgelelik gerekir.
Bu 226 rastgele bite sahip olduğunuzda, geri kalanı deterministik olarak yapılabilir ve böylece özellikleri java.util.Random
ilgisiz hale getirilebilir . İşte böyle.
Diyelim ki 52 tane oluşturuyoruz! permütasyonlar (benimle birlikte) ve sözlükbilimsel olarak sıralayın.
Permütasyon birini seçmek için hepimiz ihtiyacı arasında tek rasgele tamsayıdır 0
ve 52!-1
. Bu tam sayı, bizim 226 bit entropi. Sıralı permütasyon listemize bir indeks olarak kullanacağız. Rastgele endeks eşit dağıtılırsa, sadece onlar seçilecektir, tüm permütasyon seçilebilir olduğu garanti edilir equiprobably (soru soran olandan daha güçlü bir garanti olan).
Şimdi, tüm bu permütasyonları oluşturmanıza gerek yok. Varsayımsal sıralama listemizdeki rastgele seçilen konumu göz önüne alındığında doğrudan bir tane üretebilirsiniz. Bu Lehmer [1] kodu kullanılarak O (n 2 ) zamanında yapılabilir ( numaralandırma permütasyonlarına ve factoriadic sayı sistemine de bakınız ). Buradaki n, destenizin boyutu, yani 52'dir.
Bu StackOverflow yanıtında bir C uygulaması var . Orada n = 52 için taşacak birkaç tamsayı değişkeni vardır, ancak neyse ki Java'da kullanabilirsiniz java.math.BigInteger
. Hesaplamaların geri kalanı neredeyse olduğu gibi kopyalanabilir:
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 ile karıştırılmamalıdır . :)
Random
asla gerçek rastgele sayılar değildir. Bu bir PRNG'dir, burada P "sahte" anlamına gelir. İçin gerçek rasgele sayılar, (örneğin random.org gibi) rastgelelik kaynağı gerekir.