java生成抽样随机数的多种算法

 更新时间:2016年10月24日 09:30:51   作者:Q-WHai  
本文主要介绍了java生成抽样随机数的多种算法,主要是基于random库函数的,有需要的可以了解一下。

本章先讲解Java随机数的几种产生方式,然后通过示例对其进行演示。

概述:

这里你是不是会说,生成随机数有什么难的?不就是直接使用Java封装好了的random就行了么?当然对于一般情况下是OK的,而且本文要说明的这些算法也是基于这个random库函数的。

本文主要是针对抽样这一行为进行的,而抽样本身有一个隐含的规则就是不要有重复数据。好了,有了这些说明。你可以先尝试着用一些自己的想法来实现不重复地生成随机数。

算法尝试:

一些好的算法出现,往往伴随着一些不那么好的算法。但是对于效果不太好的算法,它们普遍有一个共性,方便理解和实现。下面是通过一个循序渐进的方式来作一个简单地说明。

第一次尝试:朴素随机算法

这个算法很好理解,就是随机!每一次产生一个随机数,并加入集合。 

 private void simpleRandom(int start, int end, int count) { 
    System.out.println("朴素随机算法:"); 
    StringBuffer buffer = new StringBuffer(); 
    for (int i = 0; i < count; i++) { 
      int random = NumberUtils.randomInteger(start, end); 
      buffer.append(i == 0 ? ("[" + random) : (", " + random)); 
    } 
    buffer.append("]"); 
    System.out.println(buffer); 
  } 

第二次尝试:检查存在性随机算法

我们知道上面的方法有一个问题,就是可能会有重复数据。于是,我们就想到,在生成一个随机数的时候进行检查一下这个数是不是已经存在了,如果存在了就重新生成。

private void checkRandom(int start, int end, int count) { 
    System.out.println("检查存在性随机算法:"); 
    StringBuffer buffer = new StringBuffer(); 
    List<Integer> save = new ArrayList<>(); 
    for (int i = 0; i < count; i++) { 
      int random = NumberUtils.randomInteger(start, end); 
      if (exits(save, random)) { 
        i--; 
        continue; 
      } 
       
      save.add(random); 
      buffer.append(i == 0 ? ("[" + random) : (", " + random)); 
    } 
    buffer.append("]"); 
    System.out.println(buffer); 
  } 

第三次尝试:元素移除随机算法

上面的算法已经解决了数据重复的问题。不过,有一个很糟糕的问题就是可能我们要花费很长的时间来生成抽样随机数(这个要看脸了。。。。)。

不过,这里我们有了新想法。那就是在一个集合中去随机一个数,当这个被选中的时候就remove掉,那么下次再随机的时候是不是就不会再随机到这个数了?这样就很好地解决了随机数的重复问题。代码如下:

 private void removeRandom(int start, int end, int count) { 
    System.out.println("元素移除随机算法:"); 
    StringBuffer buffer = new StringBuffer(); 
    List<Integer> numbers = initList(start, end); 
    for (int i = 0; i < count; i++) { 
      int random = NumberUtils.randomInteger(count - i); 
      buffer.append(i == 0 ? ("[" + numbers.get(random)) : (", " + numbers.get(random))); 
      numbers.remove(random); 
    } 
     
    buffer.append("]"); 
    System.out.println(buffer); 
  } 

第四次尝试:状态转移随机算法

在我之前的很多博客中,就有一些是算法中的状态转移过程。而状态的转移也是我最喜欢的算法之一。下面的图-1中标注了随机数的取值范围,序列中的橙色数字是结果中的随机序列。最下方的序列中有一些虚线的箭头,代表了状态的转移。

图-1 基于状态转移的抽样随机数生成算法

实现代码:

 private void statusRandom(int start, int end, int count) { 
    System.out.println("状态转移随机算法:"); 
    StringBuffer buffer = new StringBuffer(); 
    int[] status = new int[end + 1]; 
    for (int i = 0; i < count; i++) { 
      int random = NumberUtils.randomInteger(start, end); 
      System.err.println(random); 
      if (status[random] == 0) { 
        buffer.append(i == 0 ? ("[" + random) : (", " + random)); 
        status[random] = random == end ? start : (random + 1); // 不可能有在start之前的数字 
      } else { 
        // 状态转移 
        int index = random; 
        do { 
          index = status[index]; 
        } while (status[index] != 0); 
         
        buffer.append(i == 0 ? ("[" + index) : (", " + index)); 
        status[index] = index == end ? start : (index + 1); // 不可能有在start之前的数字 
      } 
    } 
     
    buffer.append("]"); 
    System.out.println(buffer); 
  } 

第五次尝试:递归Floyd随机算法

Floyd算法说到底也是一种状态的转移过程。该算法会要求输入一个List或是array来保存已经确定的随机数。顾名思义,这里我会用到递归的解法。在递归的过程中,我们把第i个随机数的状态转移到了第i-1个随机身上了。代码如下:

private List<Integer> simpleFloyd(List<Integer> list, int count, int start, int end) { 
    if (count == 0) { 
      return list; 
    } 
    list = simpleFloyd(list, count - 1, start, end - 1); 
    int random = NumberUtils.randomInteger(start, end); 
    if (list.contains(random)) { 
      list.add(end); 
    } else { 
      list.add(random); 
    } 
    return list; 
  } 

第六次尝试:迭代Floyd随机算法

思路与上面的递归Floyd随机算法是相似的,不过,这里我们加入了一个变量来做优化。就不需要再去递归了。代码如下:

private List<Integer> iterationFloyd(int start, int end, int count) { 
    System.out.println("迭代Floyd随机算法:"); 
    List<Integer> list = new ArrayList<>(); 
    for (int i = end - count + 1; i < end; i++) { 
      int random = NumberUtils.randomInteger(start, i); 
      if (list.contains(random)) { 
        list.add(i); 
      } else { 
        list.add(random); 
      } 
    } 
     
    return list; 
  } 

测试结果:

 

图-2 随机数生成算法测试结果

在上面的测试结果中,我们可以很明显地看出朴素随机算法不仅有重复数据,而且还是最耗时的。所以,在抽样的随机数生成时,避免使用这一算法。而在后几种算法中,状态转移随机算法最佳,迭代Floyd随机算法次之。这个可以根据个人偏爱来做选择。

相关文章

  • java字符串拼接与性能分析详解

    java字符串拼接与性能分析详解

    在JAVA中拼接两个字符串的最简便的方式就是使用操作符”+”。如果你用”+”来连接固定长度的字符串,可能性能上会稍受影响,但是如果你是在循环中来”+”多个串的话,性能将指数倍的下降,下面我们分析一下JAVA字符串拼接的性能
    2014-01-01
  • 彻底解决Spring mvc中时间的转换和序列化等问题

    彻底解决Spring mvc中时间的转换和序列化等问题

    这篇文章主要介绍了彻底解决Spring mvc中时间的转换和序列化等问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • Java数据结构顺序表的详细讲解

    Java数据结构顺序表的详细讲解

    大家好,今天给大家带来的是顺序表,我觉得顺序表还是有比较难理解的地方的,于是我就把这一块的内容全部整理到了一起,希望能够给刚刚进行学习数据结构的人带来一些帮助,或者是已经学过这块的朋友们带来更深的理解,我们现在就开始吧
    2022-05-05
  • springboot中shiro使用自定义注解屏蔽接口鉴权实现

    springboot中shiro使用自定义注解屏蔽接口鉴权实现

    本文主要介绍了springboot中shiro使用自定义注解屏蔽接口鉴权实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • vue 实现删除对象的元素 delete

    vue 实现删除对象的元素 delete

    这篇文章主要介绍了vue 实现删除对象的元素delete,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • spring中的BeanFactory与FactoryBean的讲解

    spring中的BeanFactory与FactoryBean的讲解

    今天小编就为大家分享一篇关于spring中的BeanFactory与FactoryBean的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • 详解如何使用Jersey客户端请求Spring Boot(RESTFul)服务

    详解如何使用Jersey客户端请求Spring Boot(RESTFul)服务

    本篇文章主要介绍了详解如何使用Jersey客户端请求Spring Boot(RESTFul)服务,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • 使用Runtime 调用Process.waitfor导致的阻塞问题

    使用Runtime 调用Process.waitfor导致的阻塞问题

    这篇文章主要介绍了使用Runtime 调用Process.waitfor导致的阻塞问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java多线程并发执行demo代码实例

    Java多线程并发执行demo代码实例

    这篇文章主要介绍了Java多线程并发执行demo代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • java中实现创建目录与创建文件的操作实例

    java中实现创建目录与创建文件的操作实例

    用Java创建文件或目录非常简单,下面这篇文章主要给大家介绍了关于java中实现创建目录与创建文件的操作实例,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01

最新评论