问题描述:
假设我们有一堆数据(可能在一个链表里,也可能在文件里),数量未知。要求只遍历一次这些数据,随机选取其中的一个元素,任何一个元素被选到的概率相等。O(n)时间,O(1)辅助空间(n是数据总数,但事先不知道)。
引例:
5个人抽5个签,只有一个签意味着“中签”,轮流抽签,从很久很久以前我们就认为这个是非常公平的例子,这个应该不用去怀疑吧。如果怀疑了,好吧,看下面的分析:
分析:
设Ai为第i个人抽签抽中事件,P(Ai)表示第i个人中签的概念,依题意得:
第一个人中签概率:P(A1)=1/5;
第二个人中签概率:P(A2)=4/5*1/4=1/5;
(因为第二个人是在第一个人选了之后才去抽的,所以,还有4/5是第一个没有选中的,剩下的四个再去抽一个所以是1/4)
同理, P(A3)=4/5*3/4*1/3=1/5;
P(A4)=4/5*3/4*2/3*1/2=1/5;
P(A3)=4/5*3/4*2/3*1/2*1=1/5;
就样,五个人抽到的是等概率的,相信了吧。
总结公式:
【面试|单遍历取等概率随机数问题】
文章图片
java产生随机数的几种方式
1.使用Math.random()方法来产生一个随机数,这个产生的随机数是0-1之间的一个double,可以乘以一定的数,比如说乘以100,他就是个100以内的随机。(j2me没有)
2.在java.util这个包里面提供了一个Random的类,我们可以新建一个Random的对象来产生随机数,他可以产生随机整数、随机float、随机double,随机long,这个也是我们在j2me的程序里经常用的一个取随机数的方法。
3.在我们的System类中有一个currentTimeMillis()方法,这个方法返回一个从1970年1月1号0点0分0秒到目前的一个毫秒数,返回类型是long,我们可以拿他作为一个随机数,我们可以拿他对一些数取模,就可以把他限制在一个范围之内。
一般会用到java.util里面包中的,一个例子:
public class Test {
public static void main(String[] args) {
java.util.Random r=new java.util.Random();
for(int i=0;
i<100;
i++){
System.out.println("i="+i+""+r.nextInt(100));
}
}
}
结果打印了100个0到100(不包含100)的随机数。
看一下Random类:
还有nextFloat().nextLong()等方法,重点看看nextInt(int n)方法吧,看jdk说明文档。
参数: n - 要返回的随机数的范围。必须为正数。 返回: 下一个伪随机数,在此随机数生成器序列中 0 (包括)和 n (不包括)之间均匀分布的 int 值。 抛出: IllegalArgumentException - 如果 n 不是正数 |
单遍历取等概率随机数问题求解JAVA实现:
public class Test {
public static void main(String[] args) {
java.util.Random r=new java.util.Random();
int n= 0 ;
int[] a = {12,34,56,78,9,91,87,4,5,62,12,3,41,8,89,541,11,54,36};
int selectNum = 0 ;
for(int k : a){
n++ ;
if(r.nextInt(n) == 0){
selectNum = k ;
}
}
System.out.println("selectNum:"+selectNum);
}
}
参考:
http://www.gocalf.com/blog/random-selection.html
http://www.blogjava.net/cool2009/archive/2009/03/15/259882.html
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- 分析COMP122 The Caesar Cipher
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- linux笔记|linux 常用命令汇总(面向面试)