随机数字生成器(Random Number Generation)看这一篇就够了!

随机数字生成器(Random Number Generation)看这一篇就够了!

伪随机数

作为utils包的一部分,Java提供了一个基本的伪随机数生成器,适当地命名为random。该对象可用于生成任意内置数字数据类型(int、float等)的伪随机值。您也可以使用它来生成一个随机布尔值,或一个随机的字节数组。示例用法如下:

import java.util.Random; ... Random random = new Random(); int randInt = random.nextInt(); long randLong = random.nextLong(); double randDouble = random.nextDouble(); //返回的值在 0.0 和 1.0 float randFloat = random.nextFloat(); //同上 byte[] randBytes = new byte[16]; random.nextBytes(randBytes); //nextBytes获取用户提供的字节数组,并用随机字节填充它。它什么也不返回。 

注意:此类只生成质量相当低的伪随机数,不应用于为加密操作或其他高质量随机性至关重要的情况生成随机数(为此,您需要使用SecureRandom类,如下所述)。对“安全”和“不安全”随机性之间区别的解释超出了本例的范围。

特定范围内的伪随机数

Random的方法nextInt(int bound)接受一个上排他边界,即返回的随机值必须小于的数字。但是,只有nextInt方法接受绑定;nextLong、nextDouble等。

Random random = new Random(); random.nextInt(1000); // 0 - 999 int number = 10 + random.nextInt(100); // 10~109 

从Java 1.7开始,您还可以使用ThreadLocalRandom(源代码(打开新窗口))。这个类提供了一个线程安全的PRNG(伪随机数生成器)。注意,这个类的nextInt方法同时接受上界和下界。

import java.util.concurrent.ThreadLocalRandom; // nextInt通常不包括最高值, // 所以加1使其包含 ThreadLocalRandom.current().nextInt(min, max + 1); 

注意,官方文档指出当绑定数据接近 2^30+1 时可能会有错误。
这个算法有点棘手。它拒绝会导致不均匀分布的值(因为231不能被n整除)。一个值被拒绝的概率取决于n。最坏的情况是n=230+1,其中拒绝的概率是1/2,循环终止前的预期迭代次数是2。
总之,指定绑定将(略微)降低nextInt方法的性能,并且随着绑定接近最大int值的一半,这种性能下降将变得更加明显。

生成加密安全的伪随机数

Random和ThreadLocalRandom对于日常使用来说已经足够好了,但它们有一个大问题:它们基于线性同余生成器(不解释咯),这是一种可以很容易预测输出的算法。因此,这两个类不适合用于加密用途(例如密钥生成)。

在需要输出非常难以预测的PRNG的情况下,可以使用java.security.SecureRandom。预测此类实例创建的随机数足以将该类标记为加密安全。

import java.security.SecureRandom; import java.util.Arrays; public class Foo { 
    public static void main(String[] args) { 
    SecureRandom rng = new SecureRandom(); byte[] randomBytes = new byte[64]; rng.nextBytes(randomBytes); //用随机字节填充randomBytes System.out.println(Arrays.toString(randomBytes)); } } 

除了加密安全之外,SecureRandom的周期为2^160,而Randoms的周期为2 ^48。然而,它的一个缺点是比Random和其他线性PRNG如Mersenne Twister和Xorshift慢得多。

注意,SecureRandom实现同时依赖于平台和提供商。默认的SecureRandom(由SUN提供商在SUN.security.produle.SecureRandom中提供):

  • 在类Unix系统上,以/dev/random和/或/dev/urandom的数据为种子。
  • 在Windows上,以CryptoAPI中对CryptGenRandom的调用为种子

用指定种子生成随机数

//创建种子为12345的随机实例。 Random random = new Random(12345L); //获取ThreadLocalRandom实例 ThreadLocalRandom tlr = ThreadLocalRandom.current(); //设置实例的种子。 tlr.setSeed(12345L); 

使用相同的种子生成随机数每次都会返回相同的数字,因此如果您不想最终得到重复的数字,为每个random实例设置不同的种子是个好主意。

System.currentTimeMillis()是获取每次调用都不同的Long的一个好方法

Random random = new Random(System.currentTimeMillis()); ThreadLocalRandom.current().setSeed(System.currentTimeMillis()); 

选择不重复的随机数

/ * 返回一个不重复的随机数数组 * @param range 如果是100,那么它可以是1-100之间的任何数字 * @param length 随机数数组的长度 * @return 无重复的随机数数组 */ public static int[] getRandomNumbersWithNoDuplicates(int range, int length){ 
    if (length<range){ 
    // 这是所有的随机数 int[] randomNumbers = new int[length]; // 循环遍历所有随机数以设置它们 for (int q = 0; q < randomNumbers.length; q++){ 
    // 获取剩余的数字 int remainingNumbers = range-q; // 从剩余的数字中获取一个新的随机数 int newRandSpot = (int) (Math.random()*remainingNumbers); newRandSpot++; // 循环所有的数字 for (int t = 1; t < range+1; t++){ 
    // 判断该数字是否已经存在 boolean taken = false; for (int number : randomNumbers){ 
    if (t==number){ 
    taken = true; break; } } // 如果不存在,则剩余的数字-- if (!taken){ 
    newRandSpot--; // 如果都判断完了,则返回 if (newRandSpot==0){ 
    randomNumbers[q] = t; } } } } return randomNumbers; } else { 
    // 无效的长度不能大于可能的数字范围 } return null; } 

该方法的工作原理是循环遍历一个具有所请求长度大小的数组,并找到可能数字的剩余长度。它将这些可能的数字中的一个随机数设置为newRandSpot,并在剩余的未取数字中找到该数字。它通过循环遍历范围并检查该数字是否已经被取下来实现这一点。

例如,如果范围是5,长度是3,并且我们已经选择了数字2。然后我们有4个剩余的数字,所以我们得到一个介于1和4之间的随机数,然后我们跳过已经使用的任何数字(2)在范围(5)中循环。

现在让我们假设在1和4之间选择的下一个数字是3。在第一个循环中,我们得到尚未取的1,因此我们可以从3中删除1,使其为2。现在在第二个循环中,我们得到2,它已经被取下了,所以我们什么也不做。我们遵循这个模式,直到我们到达4,其中一旦我们删除1,它就变成0,所以我们将新的randomNumber设置为4。

使用apache-common lang3生成随机数

这里我们使用 org.apache.commons.lang3.RandomUtils 生成单行随机数。

int x = RandomUtils.nextInt(1, 1000); 

方法nextInt(int startInclusive,int endExclusive)采用一个范围。

除了int,我们还可以使用这个类生成随机的long、double、float和字节。

RandomUtils类包含以下方法-

static byte[] nextBytes(int count) static double nextDouble() static double nextDouble(double startInclusive, double endInclusive) static float nextFloat() static float nextFloat(float startInclusive, float endInclusive) static int nextInt() static int nextInt(int startInclusive, int endExclusive) static long nextLong() static long nextLong(long startInclusive, long endExclusive) 

总结

没有什么是真正随机的,因此javadoc将这些数字称为伪随机的。这些数字是用伪随机数生成器创建的

2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/117046.html

(0)
上一篇 2024年 6月 20日 下午5:56
下一篇 2024年 6月 20日 下午6:06

相关推荐

关注微信