当前位置: 首页 > 面试题库 >

java.util.Random真的那么随机吗?我该如何生成52!(部分)可能的顺序?

欧阳斌
2023-03-14
问题内容

我一直在Random (java.util.Random)洗牌52张牌。有52个!(8.0658175e +
67)的可能性。但是,我发现for的种子java.util.Random是a long,它的大小要小得多,为2 ^ 64(1.8446744e +
19)。

从这里开始,我怀疑是否java.util.Random 真的那么随机 ;实际上有能力生成全部52个!可能性?

如果没有,我如何可靠地生成可以产生全部52个更好的随机序列!可能性?


问题答案:

选择一个随机排列同时需要比您的问题所暗示的更多或更少的随机性。让我解释。

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

您的方法的根本缺陷在于,它试图使用64位熵(随机种子)在〜2 226种可能性之间进行选择。要在〜2
226种可能性之间进行合理选择,您将必须找到一种方法来生成226位(而不是64位)熵。

有几种产生随机位的方法:专用硬件,CPU指令,OS接口,在线服务。在您的问题中已经存在一个隐含的假设,即您可以某种方式生成64位,因此只需将您打算做的事情做四次,然后将多余的位捐赠给慈善机构即可。:)

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

一旦有了这226个随机位,其余的就可以确定地完成,因此 的属性java.util.Random可以变得无关紧要。这是怎么回事。

假设我们生成了全部52个!排列(与我同在)并按字典顺序对其进行排序。

要选择一种排列,我们需要的是0和之间的一个随机整数52!-1。这个整数是我们的226位熵。我们将其用作排列的排序列表的索引。如果随机索引是均匀分布的,不仅可以保证可以选择所有排列,还可以等
概率 选择 它们 (这比问题要强得多)。

现在,您实际上不需要生成所有这些排列。给定一个在我们假设的排序列表中随机选择的位置,您可以直接生产一个。可以使用Lehmer
[1]代码在O(n
2)时间内完成此操作(另请参见编号置换和阶乘数字系统)。这里的n是甲板的大小,即52。

这个StackOverflow答案中有一个C实现。有几个整数变量会在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混淆。:)



 类似资料:
  • 我一直在使用洗牌52张牌。有52个!(8.0658175e 67)可能性。然而,我发现是一个,在2^64(1.8446744e 19)时要小得多。 从这里,我怀疑是否真的是随机的;它真的能够产生所有52个!可能性吗? 如果不是,我如何可靠地生成一个更好的随机序列,可以产生所有52个!可能性?

  • 问题内容: 我正在学习python的随机模块。而且我知道它会生成伪随机数。它的核心思想是使用高频时钟作为种子,然后使用一个函数来产生“看起来像”的随机数。 据我所知,在现实世界中甚至不可能生成真实的随机数。 但是我知道Unix随机产生器引入了其他一些因素,例如鼠标移动轨迹的参数,IO响应时间,从而给随机数产生器函数带来了不确定性。通过该方法,我们可以获得比普通伪随机数更好的随机数。很难预测。 那么

  • 问题内容: 我正在阅读Math.random()javadoc,发现random只是psuedorandom。 是否有一个库(特别是java)根据随机变量(例如环境温度,CPU温度/电压等)生成随机数? 问题答案: 查看http://random.org/ RANDOM.ORG是一种真正的随机数服务,可通过大气噪​​声生成随机性。 可以在以下位置找到用于与其连接的Java库: http //sou

  • 问题内容: 我想要一个可以生成值的伪随机序列的函数,但是该序列在每次运行时都可以重复。我想要的数据必须合理地随机分布在给定的范围内,而不必是完美的。 我想根据随机数据编写一些可以对其进行性能测试的代码。我希望每台机器上的每个测试运行的数据都相同,但是出于存储原因,我不想随测试一起运送随机数据(最终可能会变成许多兆字节)。 该模块的库似乎没有说相同的种子在任何机器上总是给出相同的序列。 编辑:如果您

  • 问题内容: 有没有一种方法可以在SQL Server中生成具有定义的字符数的 随机 base36标识符? 我已经搜索并找到了许多将base 36转换为int,反之亦然的示例,但不是随机生成唯一ID的示例。 问题答案: 该解决方案有点冗长,但可以正常使用,并且可以轻松地适应各种需求。这是一些示例输出: 请注意,您需要创建一个视图来包装UDF内不允许使用的RAND。因此,此解决方案需要两个数据库对象,

  • 问题 你需要生成在一定范围内的随机数,但你也需要对发生器进行“生成种子”操作来提供可预测的值。 解决方案 编写你自己的随机数生成器。当然有很多方法可以做到这一点,这里给出一个简单的示例。 该发生器绝对不可以以加密为目的! class Rand # 如果没有种子创建,使用当前时间作为种子 constructor: (@seed) -> # Knuth and Lewis' impro