javaWIFI密码字典生成器

Java修炼终极指南:. 选择伪随机数生成器


当我们抛硬币或掷骰子时,我们说我们看到了“真正”或“自然”的随机性在工作。尽管如此,还是有一些工具假装能够预测抛硬币、掷骰子或旋转轮盘的结果,特别是当满足某些上下文条件时。计算机可以使用算法通过所谓的随机数生成器来生成随机数。由于涉及算法,生成的数字被认为是伪随机的。这就是所谓的“伪”随机性。显然,伪随机数也是可预测的。为什么?

伪随机数生成器通过播种数据开始工作。这是生成器的秘密(种子),它代表用作生成伪随机数起点的数据片段。如果我们知道算法是如何工作的以及种子是什么,那么输出就是可预测的。在不知道种子的情况下,可预测性非常低。因此,为每个伪随机数生成器选择合适的种子是一个重要步骤。

在JDK 之前,Java用于生成伪随机数的API有点模糊。基本上,我们有一个包装在众所周知的java.util.Random类中的健壮API,以及Random的两个子类:SecureRandom(密码学伪随机数生成器)和ThreadLocalRandom(非线程安全伪随机数生成器)。从性能角度来看,这些伪随机数生成器之间的关系是:SecureRandom比Random慢,Random比ThreadLocalRandom慢。

除了这些类之外,我们还有SplittableRandom。这是一个非线程安全的伪生成器,能够在每次调用其split()方法时生成一个新的SplittableRandom。这样,每个线程(例如,在fork/join架构中)都可以使用自己的SplittableGenerator。

直到JDK ,伪随机数生成器的类层次结构如下图所示:


图 – JDK 之前的Java伪随机数生成器的类层次结构

如图所示,在伪随机数生成器之间切换或选择不同类型的算法确实非常繁琐。看看那个SplittableRandom——它迷失在无人区。

从JDK 开始,我们拥有了一个更灵活和强大的API来生成伪随机数。这是一个基于接口的API(随JEP 一起发布),围绕新的RandomGenerator接口展开。以下是JDK 的增强类层次结构:


图 – 从JDK 开始的Java伪随机数生成器的类层次结构

RandomGenerator接口代表了此API的顶峰。它代表了一个用于生成伪随机数的通用和统一协议。这个接口接管了Random API并添加了一些其他内容。

RandomGenerator接口通过5个子接口扩展,旨在为5种不同类型的伪随机数生成器提供特殊协议。

* StreamableGenerator 可以返回RandomGenerator对象的流

* SplitableGenerator 可以从这个生成器返回一个新的生成器(分裂自身)

* JumpableGenerator 可以跳过中等数量的抽取

* LeapableGenerator 可以跳过大量的抽取

*
ArbitrarilyJumpableGenerator 可以跳过任意数量的抽取

获取默认的RandomGenerator可以这样做(这是开始生成伪随机数的最简单方法,但你对选择什么没有控制权):

RandomGenerator defaultGenerator   
  = RandomGenerator.getDefault();  
// 开始生成伪随机数  
defaultGenerator.nextInt/Float/...();  
defaultGenerator.ints/doubles/...();


除了这些接口之外,新API还附带了一个类(RandomGeneratorFactory),它是基于所选算法的伪随机数生成器的工厂。有三组新算法(很可能还会有更多),如下所示:

* LXM组

* Xoroshiro组

* Xoshiro组

突出显示的算法是默认算法(L32X64MixRandom)。根据伪随机数生成器的类型,我们可以选择所有/一些先前的算法。例如,L128X256MixRandom算法可以与SplittableGenerator一起使用,但不能与LeapableGenerator一起使用。所选算法与伪随机数生成器之间的不匹配会导致IllegalArgumentException。以下图表可以帮助您决定使用哪种算法。


图 – JDK 随机生成器的算法及其属性

此图表是通过以下代码生成的,旨在列出所有可用的算法及其属性(可流式处理、可跳跃、统计等):

Stream> all   
     = RandomGeneratorFactory.all();  
Object[][] data = all.sorted(Comparator.comparing(  
                   RandomGeneratorFactory::group))  
  .map(f -> {  
     Object[] obj = new Object[]{  
       f.name(),  
       f.group(),  
       f.isArbitrarilyJumpable(),  
       f.isDeprecated(),  
       f.isHardware(),  
       f.isJumpable(),  
       f.isLeapable(),  
       f.isSplittable(),  
       f.isStatistical(),  
       f.isStochastic(),  
       f.isStreamable()  
     };  
     return obj;  
  }).toArray(Object[][]::new);


可以通过名称或属性轻松选择算法。

按名称选择算法

可以通过一组静态的of()方法按名称选择算法。RandomGenerator和RandomGeneratorFactory中都有一个of()方法,可用于为特定算法创建伪随机数生成器,如下所示:

RandomGenerator generator   
  = RandomGenerator.of("L128X256MixRandom");  
RandomGenerator generator   
  = RandomGeneratorFactory.of("Xoroshiro128PlusPlus")  
                          .create();


接下来,我们可以通过调用众所周知的API(ints()、doubles()、nextInt()、nextFloat()等)来生成伪随机数。

如果我们需要特定的伪随机数生成器和算法,那么我们可以使用该生成器的of()方法,如下所示(这里,我们创建一个LeapableGenerator):

LeapableGenerator leapableGenerator   
  = LeapableGenerator.of("Xoshiro256PlusPlus");  
LeapableGenerator leapableGenerator = RandomGeneratorFactory  
  .of("Xoshiro256PlusPlus").create();

在SplittableRandom的情况下,您也可以使用构造函数,但不能指定算法:

SplittableRandom splittableGenerator = new SplittableRandom();


在附带的代码中,您可以看到更多示例。

按属性选择算法

正如您在图中看到的,算法具有一组属性(是否可跳跃、是否统计等)。让我们选择一个既是统计又是可跳跃的算法:

RandomGenerator generator = RandomGeneratorFactory.all()  
  .filter(RandomGeneratorFactory::isLeapable)  
  .filter(RandomGeneratorFactory::isStatistical)  
  .findFirst()  
  .map(RandomGeneratorFactory::create)  
  .orElseThrow(() -> new RuntimeException(  
       "Cannot find this kind of generator"));


返回的算法可能是Xoshiro256PlusPlus。

原文链接:,转发请注明来源!