【问题标题】:Java random always returns the same number when I set the seed?当我设置种子时,Java random 总是返回相同的数字?
【发布时间】:2011-07-28 20:08:02
【问题描述】:

我需要有关我正在创建的随机数生成器的帮助。我的代码如下(在一个名为 numbers 的类中):

public int random(int i){
    Random randnum = new Random();
    randnum.setSeed(123456789);
    return randnum.nextInt(i);
}

当我从另一个类调用这个方法时(为了生成一个随机数),它总是返回相同的数字。例如,如果我要这样做:

System.out.println(numbers.random(10));
System.out.print(numbers.random(10));

它总是打印相同的数字,例如5 5. 我该怎么做才能打印两个不同的数字,例如5 8

我必须设置种子。

谢谢

【问题讨论】:

  • 不要在每次调用函数时创建新的 Random 对象。将其保留为私有变量并仅实例化一次
  • 你把种子设置成什么?
  • 不建议手动设置种子,除非您确切知道它的含义。
  • “任何考虑产生随机数字的算术方法的人当然都处于罪恶状态”

标签: java random


【解决方案1】:

有两个问题导致您看到的内容。首先是代码为 Random 实例设置种子值。第二个是实例方法“random”实例化一个新的 Random 对象,然后每次都立即将其种子设置为相同的种子。这两者的结合保证了对于相同的 i 值,“随机”方法将始终返回相同的值,并且它始终是种子始终生成的序列中的第一个。

假设设置种子是强制性的,为了获取序列中的下一个值而不是每次序列的相同第一个值,Random 的 randnum 实例不能在其下一个方法获取之前每次都设置其种子叫。要解决此问题,请将 Random 的 randnum 局部变量实例从随机实例方法的范围移动到类范围。其次,仅在为 random 分配一个 Random 实例或仅从它获得相同的结果序列以重新开始时才设置种子。类 Random 的 setSeed(long seed) 实例方法不能在类范围内执行,因此构造函数必须使用带有 long seed 参数的 Random 构造函数来设置它。以下代码显示了更改:

public class RandomDemo { // arbitrary example class name
    // lots of class related stuff may be here...

    // still inside the class scope...
    // private is a good idea unless an external method needs to change it
    private Random randnum = new Random(123456789L);
    // the seed guarantees it will always produce the same sequence
    // of pseudo-random values when the next methods get called
    // for unpredicable sequences, use the following constructor instead:
    // private Random randnum = new Random();

    // lots of code may be here...

    // publicly exposed instance method for getting random number
    // from a sequence determined by seed 123456789L
    // in the range from 0 through i-1
    public int randnum(int i) {
        // don't set the seed in here, or randnum will return the exact same integer
        // for the same value of i on every method call
        // nextInt(i) will give the next value from randnum conforming to range i
        return randnum.nextInt(i);
    } // end randnum

    // lots of more code may be here...

} // end class RandDemo

如前所述,上述内容将为您的确切问题提供准确的解决方案。然而,考虑到它的作用,使用强制种子似乎不寻常。

如果这是用于序列必须可预测和可重复的课程项目或软件测试,则将种子设置为固定值是有意义的。否则,质疑将种子设置为某个预定值的有效性。下面解释了更多关于 Random、Random 的种子以及为什么有提供种子的规定。

Random 有两个构造函数:

Random()

Random(long seed)

还有一个实例方法

setSeed(long seed)

所有这些都会影响从 Random 实例获得的数字序列。实例方法,

setSeed(long seed)

将 Random 对象设置为与它刚刚使用与构造函数参数相同的种子实例化时的相同状态。仅使用种子值的低 48 位。

如果 Random 对象在没有种子的情况下被实例化,则种子将与系统时间相同,以毫秒为单位。这确保了除非两个 Random 对象在同一毫秒内被实例化,否则它们将产生不同的伪随机序列。仅使用种子值的低 48 位。这会导致不可预测的伪随机序列。每次调用下一个方法时都不需要和浪费计算资源来获取一个新的 Random 实例。

提供了 Random 的种子参数,以便可以实例化一个 Random 对象,该对象产生可重复的序列。对于给定的种子,无论何时使用该种子,都保证下一个方法中的值序列是相同的序列。这对于测试将使用伪随机序列的软件很有用,其中结果必须是可预测和可重复的。在操作中创建不同的不可预测的伪随机序列是没有用的。

“我必须设置种子”这句话否定了 Random 对象的伪随机序列的任何不可预测性。这是针对课程项目或软件测试,其中程序的相同输入的结果必须相同?

【讨论】:

    【解决方案2】:

    您是否一定需要在您的random(int i) 方法中创建new Random()?如果您有义务这样做,您可以使用,您可以将种子设置为当前时间,尽管这不是失败证明,因为您可以如此快速地调用您的numbers.random(10),以至于它最终会成为相同的种子。您可以尝试使用 nanoSeconds(我认为是 System.nanoTime()?如果 setSeed 只接受 int,我猜是乘以它)。

    但是,如果您被允许这样做,我的建议是在您的方法之外声明您的 Random 。如果您在 number 类构造函数中实例化您的随机变量,您可以设置任何种子,并且任何时候调用您的方法都会给您一个新数字。 (如果您使用常量种子,则每次重新启动应用程序时它们将是相同的数字集,但是在这种情况下您也可以使用时间作为种子)。

    最后,如果您同时声明多个number 类,则可能是最后一个问题。它们都将具有相同的随机种子,并为您提供相同的随机数集。如果发生这种情况,您可以在主类中创建一个static Random,然后在您的数字类中调用它。虽然这会将这两个类结合起来,但它会起作用。另一种选择是将递增值发送到您的 number 类构造函数,对于您实例化的每个 number,并使用您传递的值作为种子。

    如果允许您这样做,第二个选项应该对您有好处。

    【讨论】:

      【解决方案3】:

      通常,Random 不是真正的随机,而是伪随机。这意味着它需要一个给定的种子并使用它来生成一个看起来像随机的数字序列(但它是完全可预测的,如果你放置相同的种子,它就会重复)。

      如果你不放种子,那么第一个种子将取自可变源(通常是系统时间)。

      通常,将使用带有种子的值以使其重复精确值(例如,用于测试)。改用没有种子的 Random。

      【讨论】:

        【解决方案4】:

        您使用的不是随机数生成器,而是伪随机数生成器。 PRNG 生成伪随机数序列,种子在序列中选择一个起点(一个 PRNG 可能生成一个或多个序列)。

        【讨论】:

          【解决方案5】:

          您需要在全班共享Random() 实例:

          public class Numbers {
              Random randnum;
          
              public Numbers() {
                  randnum = new Random();
                  randnum.setSeed(123456789);
              }
          
              public int random(int i){
                  return randnum.nextInt(i);
              }
          }
          

          【讨论】:

            【解决方案6】:

            在启动时设置一次种子,而不是每次你想要一个新的随机数时。

            【讨论】:

              【解决方案7】:

              如果您始终设置种子,您将始终得到相同的答案。这就是设置种子的作用。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-09-20
                • 2014-09-14
                • 1970-01-01
                相关资源
                最近更新 更多