生成大量随机数的有效方法

时间:2015-09-22 19:20:17

标签: java random secure-random

我有一个java方法,必须在很短的时间内生成大量的随机数。 我的第一种方法是使用Math.random(它的工作速度非常快),但我有一个假设,因为我将Math.random这么快地调用在另一个后面,"随机"由于这个原因,它并不是随机的(或者更少随机)(但我需要它尽可能随机)。

我现在有两个问题:

  1. 我的推定是正确的,因为在很短的时间内随机输出的随机输出次数减少了吗? 如果1.的答案是肯定的:
  2. 以较少的随机性删除问题的最快方式(每次通话)是什么?
  3. 我已经玩过SecureRandom,但它比普通的Math.random慢了15倍,这对我的要求来说太慢了。

7 个答案:

答案 0 :(得分:4)

TL; DR:你的推定是错误的。

Math.randomjava.util.Random上的单个实例采取行动:

  

返回带有正号的double值,大于或等于   0.0且小于1.0。返回值是伪随机选择的,具有来自该范围的(近似)均匀分布。

     

这个方法的时候   首先调用它,它会创建一个新的伪随机数   生成器,就像表达式

一样      

new java.util.Random()

来自JavaDoc

现在,java.util.Random使用的linear congruential formula播种的数字“很可能与此构造函数的任何其他调用都不同。 1

由于这是伪随机进展 - 即它将从同一种子中提供完全相同的值 - 从Math.random提取数字的速度对其随机性没有影响。

答案 1 :(得分:3)

使用Random类的随机数使用一种算法,该算法会对一个int进行修改,以便为您提供一个新的int。无论您调用它的速度有多快或多少次,它都将使用相同的算法。进展是进展。

要对此进行测试,请使用数字播种,例如42.然后观察进度。再次使用相同的数字播种它。同样的进展。

这种方法的缺点是数字不是TRULY随机的。它们非常随机,对大多数事情都足够好,但不是完全随机的。

我通过模拟硬盘测试运行Random方法的输出。它通过了大部分的飞行颜色,一个是边缘线,一个只是平坦的失败。这就是我们正在谈论的随机性。

另外,因为它使用日期时间戳来播种自身,所以在某些情况下它可以预测。想象一下那个星期一早上开始运行你的任务的人,那个星期的第一件事。有一些可预测性,因为它将以星期一早上8到8:30之间的时间戳运行。

因此,对于大多数与安全性无关的操作,Random足够好。甚至很多。

另一方面,SecureRandom将生成真正随机的数字。它通过查看基于无数因素的系统时序和其他因第二到第二而变化的事情来做到这一点。

缺点是这些因素只会在一秒钟内发生变化,因此SecureRandom只能在一段时间内生成有限数量的随机数。它确实尝试提前生成一些并缓存它们以供使用,但是你可以吹掉缓存。

通过这种方式,它就像我的反渗透水过滤器。它含有已经过滤的一加仑水。如果你一次性使用全加仑水,那么你就可以按照它过滤它的速度 - 例如每5秒1盎司或者其他一些。第一加仑很快,然后真的很慢。

答案 2 :(得分:1)

如果您可以使用Java8,我建议使用java.utils.SplitableRandom。它更快,具有更好的统计分布。在我的测试中,java.utils.SplitableRandom比java.utils.Random快30倍。

我用tobijdc answer来写这个答案。

答案 3 :(得分:0)

  1. (伪)随机数发生器在给定相同初始种子值的情况下产生相同的结果,而不管其被调用的频率如何。它完全是确定性的,与速度无关。种子的选择取决于时间(如果没有明确指定),但不取决于生成的序列。
  2. 如果您需要更快的速度,您可以预先计算大于您需要的长度的伪随机数序列的值,然后只使用一次调用生成器来选择序列中的起始位置。这样,您可以在所有后续运行的一次初始调用后简单地读出值。您的性能将受到索引和读取持有该表的内存的速度的限制。根据您的应用,可能不建议重复使用序列。

答案 4 :(得分:0)

虽然Random可能足够好,但您可以通过使用更接近您需要的功能来改进Math.random()。 e.g。

Random rand = new Random();

for ( loop ) {
   int dice = rand.nextInt(6) + 1;

这比使用Math.random()要快得多,但如果您需要long

long l = rand.nextLong();

在这种情况下l具有64位随机性,但Math.random()最多具有53位(实际上它只有48位)

答案 5 :(得分:0)

如果您需要快速获得大量数字(如模拟或蒙特卡罗集成),那么密码安全RNG将不够快。 java.util.Random()很快,但PRNG质量很差。您需要的是高质量的快速PRNG,如Mersenne Twister或XorShift。例如,查看http://xorshift.di.unimi.it/或我自己的ojrandlib。

答案 6 :(得分:0)

尝试使用java kissAESPRNG内部生成器。它是线程安全的,在批量请求中使用时速度大约是Random的两倍,并且可以产生128位加密强(但可重复,如果重置种子)伪随机数。它基于AES CTR模式,在大多数系统中都经过高度优化。

kiss.util.AESPRNG prng = new kiss.util.AESPRNG();
double [] x = new double [1_000_000];
prng.nextDoubles(x,0,x.length);

如果需要可重复序列,请使用seed(byte [] value16)或seed(double value)。重置序列。它是Random的替代品,但是对于范围或批量数字有许多便利方法。它实际上比任何建议的替代方案都要好:快速,可重复和128位强随机。