【问题标题】:Generate random integers in java在java中生成随机整数
【发布时间】:2011-03-19 07:13:06
【问题描述】:

如何生成随机整数但确保它们不会重复?

现在我使用:

Random randomGenerator = new Random();
randomGenerator.nextInt(100);

EDIT I

我正在寻找最有效或最不坏的方法

EDIT II

范围不重要

【问题讨论】:

  • 我明白你的意思,但作为术语的挑剔,如果你对伪随机序列施加唯一性约束,它就不再是真正的“随机”。
  • @Gian 你是对的,但它更容易写随机然后唯一/不可重复的数字或任何正确的词
  • 正如 Stephen C. 在下面的回答中正确观察到的那样,您基本上是在尝试生成(非连续?)整数序列的排列。
  • @Jherico 这是在 java 中标记的

标签: java


【解决方案1】:
ArrayList<Integer> list = new ArrayList<Integer>(100);
for(int i = 0; i < 100; i++)
{
  list.add(i);
}
Collections.shuffle(list);

现在,list 包含数字 0 到 99,但顺序是随机的。

【讨论】:

  • 只要您的整数范围足够小(否则您预先分配的数组将非常昂贵),这将非常有效。
  • 如果您想要已知范围内的随机整数,这是最好的解决方案。将此代码包装到您自己的“随机”类中,并提供一个 next() 方法,该方法委托给静态 Iterator 实例,您就可以了。或者,使用 LinkedList 并像队列/堆栈一样不断地从中取出/弹出。
  • @David Underhill,没错,但是按照您的建议保留一个 HashTable 有同样的问题。
  • @Tim:没错,它最终必须为整个空间分配内存——但前提是你需要生成范围内的每个随机数。在只有 100 个选择的情况下,预先分配数组很好(并且是一个更好的解决方案 imo)。但是,如果您需要从 [0,2**64) 之类的范围内生成数字,那么您将永远无法完成数组的预分配 :)。
  • 这些收集方法很疯狂。线性反馈移位寄存器将在常量内存中执行此操作。
【解决方案2】:

如果您想要的是伪随机非重复数字序列,那么您应该查看linear feedback shift register。它将产生 0 到给定 2 的幂之间的所有数字,而不会重复。您可以通过选择最接近的 2 的较大幂并丢弃 N 上的所有结果来轻松地将其限制为 N。它没有此处其他基于 colleciton 的解决方案所具有的内存限制。

你可以找到java实现here

【讨论】:

  • 我不敢相信这没有高于两个顶级答案,随着 N 的升高,这两个答案都具有可怕的内存和/或运行时性能。
  • 我看到的唯一问题是 LFSR 每个长度将有 1 个单个序列(即使您可以更改起点)。因此,对于特定的 LFSR 抽头设置,您将始终拥有 34 个,然后是 78 个,用于 100 长度的序列。有什么解决方法吗?
【解决方案3】:

如何生成随机整数但确保它们不会重复?

首先,我想指出,数字不重复的限制使得它们根据定义是非随机的。

我认为您真正需要的是在某个范围内随机生成的数字排列;例如099。即便如此,一旦您使用了该范围内的所有数字,重复也是不可避免的。

显然,您可以增加范围的大小,以便获得更大的数字而无需任何重复。但是当您这样做时,您会遇到一个问题,即您的生成器需要记住所有以前生成的数字。对于大的N,这会占用大量内存。

记住大量数字的替代方法是使用具有长周期长度的伪随机数生成器,并将生成器的整个状态作为“随机”数返回。这保证没有重复的数字......直到生成器循环。

(这个答案可能超出了 OP 感兴趣的范围......但有人可能会觉得它很有用。)

【讨论】:

    【解决方案4】:

    如果您的整数范围非常大(>>100),那么您可以将生成的整数放入哈希表中。生成新的随机数时,请继续生成,直到得到一个不在哈希表中的数字。

    【讨论】:

    • 这个实现会产生一个随着时间增长的泄漏。此外,Java HashTables 不喜欢超过 80% 的满,所以这会泄漏更多的内存。
    • 是的,但是如果您不希望有任何重复并且您有大量的数字,那么您没有太多选择:生成器必须记住它以某种方式生成的数字。
    • @Tim Bender,这不是“泄漏”,只是内存使用效率低
    【解决方案5】:

    根据应用程序,您还可以生成一个严格递增的序列,即从一个种子开始并向其添加一个范围内的随机数,然后将该结果重新用作下一个数字的种子。您可以通过调整范围来设置它的可猜测性,并与您需要的数字数量相平衡(如果您进行了高达例如 1,000 的增量步骤,那么您不会很快耗尽 64 位无符号整数,例如)。

    当然,如果您试图在密码学意义上创建某种不可猜测的数字,这是非常糟糕的,但是具有非重复序列可能会对基于它的任何密码提供相当有效的攻击,所以我'我希望你没有在任何类型的安全环境中使用它。

    也就是说,这种解决方案不容易受到其他一些人建议的定时攻击。

    【讨论】:

      【解决方案6】:

      Matthew Flaschen 的解决方案适用于小数字。如果您的范围真的很大,最好使用某种Set 来跟踪使用过的数字:

      Set usedNumbers = new HashSet();
      Random randomGenerator = new Random();
      int currentNumber;
      while(IStillWantMoreNumbers) {
          do {
              currentNumber = randomGenerator.nextInt(100000);
          } while (usedNumbers.contains(currentNumber));
      }
      

      不过,您必须小心这一点,因为随着“已使用”数字的比例增加,此函数所花费的时间将成倍增加。如果您的范围远大于您需要生成的数字量,这确实是一个好主意。

      【讨论】:

      • 设置值不是唯一的吗?难道你不能一直添加到集合中,直到达到所需的集合大小吗?
      • 另外我认为你缺少一个添加
      【解决方案7】:

      由于没有足够的声誉,我无法评论上述较早的答案(这似乎倒退了......我不应该能够评论其他人的答案,但不能提供我自己的答案吗?...无论如何...),我想提一下,依赖 Collections.shuffle() 存在一个重大缺陷,这与集合的内存限制无关:

      Collections.shuffle() 使用 Random 对象,在 Java 中使用 48 位种子。这意味着有 281,474,976,710,656 个可能的种子值。这似乎很多。但是考虑一下如果你想用这种方法来洗一副 52 张牌。一副52张牌有52张! (超过 8*10^67 种可能的配置)。因为如果使用相同的种子,你总是会得到相同的洗牌结果,你可以看到 Collections.shuffle() 可以产生的 52 张牌的可能配置只是所有​​可能配置的一小部分。

      事实上,Collections.shuffle() 并不是一个很好的解决方案,可以将任何超过 16 个元素的集合打乱。一个 17 元素的集合有 17 个!或 355,687,428,096,000 个配置,这意味着 74,212,451,385,344 个配置永远不会是 Collections.shuffle() 的结果,对于 17 元素列表。

      根据您的需要,这可能非常重要。随机/随机化技术选择不当可能会使您的软件容易受到攻击。例如,如果您使用 Collections.shuffle() 或类似的算法来实现商业扑克服务器,那么您的洗牌将会有偏差,而精明的计算机辅助玩家可以利用这些知识为他们谋取利益,因为它会扭曲赔率。

      【讨论】:

        【解决方案8】:

        如果你想要 0 到 255 之间的 256 个随机数,生成一个随机字节,然后用它来异或一个计数器。

        byte randomSeed = rng.nextInt(255);
        for (int i = 0; i < 256; i++) {
            byte randomResult = randomSeed ^ (byte) i;
            << Do something with randomResult >>
        }
        

        适用于 2 的任何幂。

        【讨论】:

          【解决方案9】:

          如果值的范围不是有限的,那么您可以创建一个对象,该对象使用列表来跟踪使用的整数的范围。每次需要一个新的随机整数时,都会生成一个并对照使用的范围进行检查。如果整数未被使用,那么它将将该整数添加为新使用的 Range,将其添加到现有的已使用 Range,或根据需要合并两个 Range。

          但您可能真的想要 Matthew Flaschen 的解决方案。

          【讨论】:

            【解决方案10】:

            Linear Congruential Generator可以用来生成不同随机数的循环(全循环)。

            【讨论】:

              猜你喜欢
              • 2015-01-28
              • 2016-01-26
              • 2018-01-10
              • 2018-10-09
              • 2014-08-22
              • 1970-01-01
              • 2016-10-07
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多