编程珠玑之生成0至n-1之间的k个不同随机序列的扩展问题 --2014人人网笔试题目
来源:互联网 发布:历史分时图数据的下载 编辑:程序博客网 时间:2024/06/14 13:19
《编程珠玑》中习题1.4的题目是:“如果认真考虑了习题3,你将会面对生成小于n且没有重复的k个整数的问题。最简单的方法就是使用前k个正整数。这个极端的数据集合将不会明显的改变位图方法的运行时间,但是可能会歪曲系统排序的运行时间。如何生成位于0至n - 1之间的k个不同的随机顺序的随机整数?尽量使你的程序简短高效。”
解决这个问题可以使用以空间换时间的方式,基本的思想是 利用洗牌的原理,将n个数(0至n-1)按次序排好,依次让每个数和一个随机挑选出的位子进行互换,这样肯定不会重复,而且次序被打乱,具有随性。 只用交换k次,就可以取出k个小于n的互不相同的随机数。
由这个问题我们引出以下这个笔试题目的问题:
“使用一个长度为n的数组来产生随机数,当产生的随机数与数组的存在的相同则重新随机,当不同时插入到数组中,使用这种方法产生1到n的随机数的期望次数是多少次?(编程实现)”
解答:
一般的思想是产生一个随机数 arr[i] 后,和前面已经产生的arr[0]~arr[i-1]进行比较,如果有重复的就重新产生一个,该算法的平均时间复杂度为:O(n^2) ,而最坏复杂度为无限!!
这里我们按照编程珠玑上那个问题的扩展想法,利用空间换时间的算法,生成随机排列的数,此时时间复杂度为O(n)。
(1)建立一个长度为n+1的临时数组b,对该数组赋值依次赋值0~n, 即b[i] = i;
(2)取一个1~n之间的随机整数k=rand(0,n],取b[k]读入arr[i],再将b数组最后一个元素b[b.size-1]赋值给b[k],将b的长度减1;
代码实现:
/**BLOG:http://blog.csdn.net/wdzxl198*AUTHOR:Atlas*EMAIL:wdzxl198@163.com*/#include<iostream>using namespace std;void Random(int *arr,int length){srand(time(NULL));int i,randnum;int limitsize = length;int b[limitsize];for(i=0; i<limitsize; i++){ b[i] = i+1;}for(i=0; i<length; i++){ randnum = rand()%limitsize; limitsize--; arr[i] = b[randnum]; b[randnum] = b[limitsize];}}int main(){int arr[100];//假使随机出1到100的随机数Random(arr,100);for(int i=0;i<100;i++){cout<<arr[i]<<" ";}cout<<endl;quickSort(arr,0,99);for(int i=0;i<100;i++){cout<<arr[i]<<" ";}cout<<endl;system("PAUSE");return 0;}这种方法的是O(n)时间复杂度,但是没有求出题目中期望的次数。
在求期望的过程中,我发现一篇博文,题目是有一个数组,每次从中间随机取一个,然后放回去,当所有的元素都被取过,返回总共的取的次数。写一个函数实现。复杂度是什么。,这个题目与文章中的类似,转载过来分析下,
import java.util.Random; import java.util.Set; import java.util.TreeSet; /** * #面试题#有一个数组,每次从中间随机取一个,然后放回去,当所有的元素都被取过,返回总共的取的次数。 * 写一个函数实现。复杂度是什么。 * bitmap,只要一个bit就可以标记是否被取过,可参考《编程珠玑》的位图排序 * * 时间复杂度的话,我不太会算,以下是引用https://github.com/vyan/test/blob/master/accessTimes.cpp: * 使用bit打点记录已经取的数, * 复杂度分析,假设数组总长度为n * 取到第1个之前未被取到的数的期望 E(1)=1 * 取到第2个之前未被取到的数的期望 E(2)=n/n-1 * 取到第3个之前未被取到的数的期望 E(3)=n/n-2 * ... * 取到第n个之前未被取到的数的期望 E(n)=n/1 * 总得期望次数E=n+n/(n-1)+n/(n-2)+...+n/1; * =n(1+1/(n-1)+1/(n-2)+...+1/1) * =nln(n) * 所以算法平均复杂度为nlogn * * 下面的代码里面,除法和求模运算我都用位运算来实现,事实上直接用java提供的(/,%)也可以 * 同时,为了验证是否真的取到了数组的所有元素,我用了TreeSet保存已选中的下标(去重) * * 最后,还有一个问题是,可能取了很多次,都没能全选中,这个时候可以设置一个最长时间或者最大尝试次数,超过则结束程序, * 避免程序长时间运行甚至死循环 * * @author lijinnan * */ public class TimesOfAccessArray { public static final int SHIFT = 5; public static final int BLOCK_SIZE = (1 << SHIFT); //32 public static final int MASK = (1 << SHIFT) - 1; //31 public static void main(String[] args) { int[] array = new int[200]; long times = accessTimes(array); System.out.println(times); } public static long accessTimes(int[] array) { if (array == null || array.length == 0) { return -1L; } long result = 0L; int len = array.length; int[] bitmap = new int[divide(len, BLOCK_SIZE) + 1]; int setTimes = 0; Set<Integer> set = new TreeSet<Integer>(); while (setTimes < len) { int pos = new Random().nextInt(len); set.add(pos); if (set(bitmap, pos)) { setTimes++; } result++; } System.out.println(set); return result; } public static boolean set(int[] bitmap, int pos) { boolean result = false; int blockNo = divide(pos, BLOCK_SIZE); int index = mod(pos, BLOCK_SIZE); boolean notExist = (bitmap[blockNo] & (1 << index))== 0; if (notExist) { bitmap[blockNo] |= (1 << index); result = true; } return result; } public static boolean exist(int[] bitmap, int pos) { int blockNo = divide(pos, BLOCK_SIZE); int index = mod(pos, BLOCK_SIZE); return (bitmap[blockNo] & (1 << index)) != 0; } private static int divide(int x, int y) { return x >> offSet(y); } private static int mod(int x, int y) { int z = x; int i = offSet(y); return z - ((z >> i) << i); } //e.g. 32=2^5, return 5 只考虑2的幂 private static int offSet(int y) { return howManyBits(y) - 1; } //二进制的表示里面,有多少位。例如32是6位 private static int howManyBits(int y) { int bitNum = 0; while (y != 0) { y = (y >> 1); bitNum++; } return bitNum; } }
- 编程珠玑之生成0至n-1之间的k个不同随机序列的扩展问题 --2014人人网笔试题目
- 编程珠玑:1.6.4 如何生成位于0至n-1之间的k个不同的随机顺序的随机整数
- 【编程珠玑】生成k个1-n的随机数
- 快速获取[0,n]之间的k个不同的随机顺序的随机整数
- 编程珠玑之1.4生成小于n且没有重复的k个整数
- 关于随机生成N个不同整数的问题
- 随机选取一个长度为N的链表(N很大)里的K个元素 - 编程珠玑
- 《编程珠玑》第12章 抽样问题笔记 生成m个0~n间的随机数
- 随机生成n个不同的整数
- 编程珠玑之第一章习题4: 生成不同的随机顺序的随机整数及存储与读取
- 生成k个不同的随机数(1-n)
- 编程珠玑: 12章 取样问题 12.1程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复-------解题总结
- 编程珠玑: 12章 取样问题 12.1程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。 优化解法-------解题总结
- 编程珠玑: 12章 取样问题 12.3设计空间,程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。 -------解题总结
- 编程珠玑: 12章 取样问题 12.3设计空间,程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。 解法2-------解题总结
- 生成有意义随机的文本(学习文档的K阶马尔科夫链)。--- 编程珠玑
- 随机生成n个不同的数字小组合作编写
- 随机生成n个不同的数字-小组合作编写
- SNS呼叫中心的一些杂感
- 关于Eclipse不能升级android SDK和ADT的解决方法
- [intellij idea]中文显示乱码问题
- 如何将Linux命令的结果作为下一个命令的参数
- 植物大战僵尸2 V1.0.2内购问题
- 编程珠玑之生成0至n-1之间的k个不同随机序列的扩展问题 --2014人人网笔试题目
- 在centos6.4下安装php的mssql驱动
- 【Android】实现全屏、无标题栏效果
- android listview 去除滑动产生的黑影效果
- [cocos2d-x]场景动画切换过渡大全
- C++ explict关键字
- Spring 3.x企业应用开发实战(12)----基于Schema配置切面
- linux下qt编程之获取系统时间
- TabControl tab绘制关闭按钮