【问题标题】:what is the fastest way to get the nth nextInt value?获得第 n 个 nextInt 值的最快方法是什么?
【发布时间】:2019-10-11 21:36:27
【问题描述】:

我必须找到每个数组元素的第 n 个 nextInt。 下面的代码真的很慢,因为数组元素超过40k,每个数组元素都超过一百万。

int[] numbers ={1000000,1004300,2204000,1306000...40k+};
for (int i = 0; i <numbers.length; i++) {
Random ran = new Random(1234);
    int nex = 0;
    for (int n = 0; n <numbers[i]; n++) {
        nex = ran.nextInt();
    }
    System.out.println("Next int value: " + nex);
}

有没有更快的方法来获得第 100 万个 nextInt?如果是,请问怎么做?

【问题讨论】:

  • 您在第一个循环中有错字。它应该是 i++ 而不是 n++
  • 那么,您期待什么?你打电话给nextInt 至少400亿次......你真的每次都打印输出吗?因为那肯定会大大减慢整个过程
  • 打印输出并不是真正的问题,因为我可以使用 ArrayList 来捕获 nextInt,然后打印输出带有所有数字的 ArrayList。主要问题是循环 400 亿次。我在想是否存在我可以使用的 get(Index) 方法,而不是为每个元素循环数百万次。 @derpirscher
  • 通常,这些内置的随机生成器有一个状态,该状态会随每个随机变化而变化。简单来说,nexiInt 得到的随机数可能会作为下一个随机数的种子。因此,为了计算第 10 个,您需要第 9 个,为此您需要第 8 个,......可能还有其他生成器可以满足您的需求。 Java中内置的没有。
  • stackoverflow.com/a/14616408/7285198 - 可能,这就是你想要的

标签: java arrays arraylist random int


【解决方案1】:

您正在设置随机数生成器的“种子”值。这意味着每次运行这段代码都会产生相同的随机数序列,因此序列是可预测的。

如果你真的要生成随机数,那么生成第 100 万个随机数与生成第一个随机数是一样的,因为没有人能分辨出这两个不可预知的结果之间的区别。

所以,第一个潜在的优化是:不要使用可预测的序列,而是取第一个随机数。这将需要一小段恒定的时间。

接下来,如果您需要这种可重复性,您可以预先计算它们,并且您的代码将包含该结果表。然后你可以在恒定时间内查看结果。

如果您需要一个看起来有点随机的可重复序列,但不一定是这个特定序列,您可以使用不同的伪随机生成器。例如,您可以使用分组密码加密“索引”(在您的示例中为 100 万),并使用结果中的一些字节。这也是一个常数时间算法,与索引无关。

这是最后一个想法的实现:

public class PredictableSequenceGenerator {

  private final int[] numbers;

  public PredictableSequenceGenerator(int[] numbers) {
    this.numbers = Objects.requireNonNull(numbers);
  }

  public int[] generate(long seed) {
    ByteBuffer src = ByteBuffer.wrap(new byte[16]);
    ByteBuffer dst = ByteBuffer.wrap(new byte[16]);
    src.putLong(seed).putLong(seed);
    SecretKey key = new SecretKeySpec(src.array(), "AES");
    Cipher cipher;
    try {
      cipher = Cipher.getInstance("AES/ECB/NoPadding");
      cipher.init(Cipher.ENCRYPT_MODE, key);
    } catch (GeneralSecurityException ex) {
      throw new IllegalStateException("Failed to initialize generator.", ex);
    }
    int[] results = new int[numbers.length];
    for (int idx = 0; idx < numbers.length; ++idx) {
      src.clear();
      src.putInt(0, idx);
      dst.clear();
      try {
        cipher.doFinal(src, dst);
      } catch (GeneralSecurityException ex) {
        throw new IllegalStateException("Failed to transform index.", ex);
      }
      results[idx] = dst.flip().getInt();
    }
    return results;
  }

  public static void main(String... argv) {
    int[] numbers = { 1000000, 1004300, 2204000, 1306000, /* 40k+ */ };
    PredictableSequenceGenerator gen = new PredictableSequenceGenerator(numbers);
    int[] random = gen.generate(1234);
    for (int r : random)
      System.out.println(r);
  }

}

【讨论】:

  • 对不起,我没有指出种子数可能会发生变化。
  • @HitherJoe 那么如何判断结果是否是用你的算法产生的呢?
  • @HitherJoe 另一个重要的问题是你是否需要使用java.util.Random(一个线性同余生成器)来产生结果。如果你想改变种子,计算第 n 个随机数,以后能够可靠地为那个种子重新产生一个序列,那么 LCG 是困难的,但加密的想法是简单和快速的。它只是不同意java.util.Random
  • 种子值是否已经预定义?如果不是,如果你取第一个或第 1000000 个随机数,有什么区别?
  • @erickson 任何随机生成器都可以,你会推荐哪一个?
【解决方案2】:

你真的需要种子,让随机序列每次都一样吗?然后你可以对你的数字数组进行升序排序。然后对于第二个元素,您不需要重置随机生成器,但可以继续您离开的地方。这样一来,您就必须调用 nextInt 的频率与数组中的最高数字一样多,而不是 400 亿次

【讨论】:

  • 这意味着我必须在一个数组中保存数百万个数字,我认为这会占用大量内存。种子可能不一样。
  • @HitherJoe 不,他建议您使用 40k 多个元素对 numbers 进行排序。你已经在记忆中了。
  • @erickson,谢谢。现在说得通了。我会尽力做到这一点并回复你们。在此之前,如果您提出更好的解决方案,我将不胜感激。
  • 不,你对 40k 个数字进行排序。然后对于第一个随机循环,例如 1000000 次。接下来是例如 1000123。但不是循环 1000123 次,您只需要 123 次循环,因为前 1000000 次将是相同的。当然,您不能在循环之间重新初始化随机生成器。但正如你上面所说,你的种子发生了变化,这使得这个答案过时了
猜你喜欢
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 2022-08-10
  • 1970-01-01
相关资源
最近更新 更多