Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
java.util.Random真的那么随机吗?我如何生成52!(阶乘)可能序列?_Java_Permutation_Random Seed_Random - Fatal编程技术网

java.util.Random真的那么随机吗?我如何生成52!(阶乘)可能序列?

java.util.Random真的那么随机吗?我如何生成52!(阶乘)可能序列?,java,permutation,random-seed,random,Java,Permutation,Random Seed,Random,我一直在使用Random(java.util.Random)洗牌52张牌。有52个!(8.0658175e+67)可能性。然而,我发现java.util.Random的种子是一个长的,它在2^64(1.8446744e+19)时小得多 从这里开始,我怀疑java.util.Random是否真的那么随机;它是否真的能够生成所有52个!可能性 如果不是,我如何可靠地生成一个更好的随机序列,可以产生所有52个!可能性?通常,如果52项列表的最大循环长度小于226位,则伪随机数生成器(PRNG)无法从所

我一直在使用
Random(java.util.Random)
洗牌52张牌。有52个!(8.0658175e+67)可能性。然而,我发现
java.util.Random
的种子是一个
长的
,它在2^64(1.8446744e+19)时小得多

从这里开始,我怀疑
java.util.Random
是否真的那么随机;它是否真的能够生成所有52个!可能性


如果不是,我如何可靠地生成一个更好的随机序列,可以产生所有52个!可能性?

通常,如果52项列表的最大循环长度小于226位,则伪随机数生成器(PRNG)无法从所有排列中进行选择

java.util.Random
实现了一种模数为248、最大循环长度仅为48位的算法,比我提到的226位少得多。您需要使用另一个周期长度更大的PRNG,特别是最大周期长度为52阶乘或更大的PRNG

另请参见我的中的“洗牌”

该考虑独立于PRNG的性质;它同样适用于加密和非加密PRNG(当然,涉及信息安全时,非加密PRNG是不合适的)



尽管
java.security.SecureRandom
允许传入无限长度的种子,但是
SecureRandom
实现可以使用底层PRNG(例如,“SHA1PRNG”或“DRBG”)。这取决于PRNG的最大循环长度,它是否能够从52个阶乘置换中进行选择。

您的分析是正确的:在伪随机数生成器中植入任何特定的种子后,洗牌后必须产生相同的序列,将您可以获得的置换数限制为264。这个断言是通过调用
Collection.shuffle
两次,传递一个用相同种子初始化的
Random
对象,并观察两个随机shuffle是相同的

因此,解决这个问题的一个办法是使用一个随机数生成器,它允许一个更大的种子。Java提供了可以用几乎无限大小的
byte[]
数组初始化的类。然后,您可以将
SecureRandom
的实例传递给
集合。shuffle
以完成任务:

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

如果你认为这个数字只是一个位数组(或字节),那么也许你可以使用这个(安全的)代码>随机的.NestByths<代码>解决方案,然后把数组映射成<代码>新的BigType(字节[])。

选择一个随机排列需要的随机性比你的问题所暗示的要多,也要少。让我解释一下

坏消息:需要更多的随机性。

您的方法的根本缺陷是,它试图使用64位熵(随机种子)在2226种可能性之间进行选择。为了在2226种可能性之间做出公平的选择,你必须找到一种方法来产生226位的熵,而不是64位

有几种方法可以生成随机位:、、。在你的问题中已经有一个隐含的假设,即你可以以某种方式生成64位,所以只要做你想做的事情,只做四次,然后将多余的位捐给慈善机构。:)

好消息:需要更少的随机性。

一旦有了这226个随机位,其余的就可以确定地完成,因此
java.util.random
的属性可以变得无关。这里是如何

假设我们生成了全部52个!排列(请耐心听我说)并按字典顺序进行排序

要选择一个排列,我们只需要一个介于
0
52之间的随机整数-1
。这个整数就是我们的226位熵。我们将使用它作为排列排序列表的索引。如果随机索引是均匀分布的,那么不仅可以保证所有排列都可以被选择,而且它们的选择概率也是相等的(这比问题所要问的更有力的保证)

现在,您实际上不需要生成所有这些排列。考虑到它在我们假设的排序列表中随机选择的位置,您可以直接生成一个。这可以使用(另请参见和)在O(n2)时间内完成。这里的n是你甲板的大小,即52

这是一个C实现。有几个整数变量会在n=52时溢出,但幸运的是在Java中可以使用
Java.math.biginger
。其余的计算几乎可以按原样转录:

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"))));
}
publicstaticint[]shuffle(intn,biginger随机索引){
int[]perm=新的int[n];
BigInteger[]事实=新的BigInteger[n];
事实[0]=BigInteger.1;
对于(int k=1;k0;--k){
对于(int j=k-1;j>=0;--j){

如果(perm[j]让我提前道歉,因为这有点难以理解

首先,您已经知道,
java.util.Random
根本不是完全随机的。它以一种完全可预测的方式从种子生成序列。您完全正确,因为种子只有64位长,它只能生成2^64个不同的序列。如果您要以某种方式生成64个真正的随机位,那么我们如果你让他们选择一个种子,你不能用这个种子在所有52个可能的序列中以相同的概率随机选择。
// Java 7
SecureRandom random = new SecureRandom();
// Java 8
SecureRandom random = SecureRandom.getInstanceStrong();

Collections.shuffle(deck, random);
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]