编程珠玑之生成0至n-1之间的k个不同随机序列的扩展问题 --2014人人网笔试题目

简介:

  《编程珠玑》中习题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;  
    }  
     
  
} 

  

目录
相关文章
|
2月前
|
存储 监控 NoSQL
140_异步推理:队列管理框架 - 使用Celery处理高并发请求的独特设计
在大型语言模型(LLM)部署的实际场景中,推理服务的并发处理能力直接影响用户体验和系统稳定性。随着LLM应用的普及,如何高效处理大量并发请求成为部署优化中的关键挑战。传统的同步请求处理方式在面对突发流量时容易导致系统过载,响应延迟增加,甚至服务崩溃。异步推理通过引入队列管理机制,能够有效缓冲请求峰值,平滑系统负载,提高资源利用率,从而为LLM服务提供更稳定、更高效的并发处理能力。
|
3月前
|
监控 数据可视化 数据挖掘
Python Rich库使用指南:打造更美观的命令行应用
Rich库是Python的终端美化利器,支持彩色文本、智能表格、动态进度条和语法高亮,大幅提升命令行应用的可视化效果与用户体验。
265 0
|
算法 调度 开发者
多线程编程核心:上下文切换深度解析
在多线程编程中,上下文切换是一个至关重要的概念,它直接影响到程序的性能和响应速度。本文将深入探讨上下文切换的含义、原因、影响以及如何优化,帮助你在工作和学习中更好地理解和应用多线程技术。
301 4
|
9月前
|
安全 算法 区块链
当量子计算遇上区块链:未来技术的双刃剑
当量子计算遇上区块链:未来技术的双刃剑
435 16
|
Ubuntu Linux Shell
Linux系统中如何查看磁盘情况
【9月更文挑战第3天】在Linux系统中,有多种方式查看磁盘情况。可通过命令行工具`df`查看文件系统磁盘使用情况,选项`-h`以人类可读格式显示,`-T`显示文件系统类型;`du`命令显示目录或文件磁盘使用情况,`-h`以人类可读格式显示,`-s`仅显示总计;`fdisk -l`列出磁盘和分区信息。此外,图形界面的磁盘管理工具和文件管理器也可用于查看磁盘使用情况。这些方法有助于更好地管理磁盘空间。
1361 4
|
8月前
|
人工智能 文字识别 自动驾驶
突破自动驾驶"交规困境":高德&西交发布交规+高精地图基准MapDR,车道级交通规则在线理解,让AI更懂交规!
作为专业领先的出行和位置服务提供商,高德地图以数据准确率高、鲜度高著称。当前自动驾驶技术总是关注到矢量地图的构建,往往忽略了车道级驾驶规则的制作。对应图商而言,车道级的领航不仅需要有正确的车道级矢量表达,还要明确每条路的驾驶规则,保证引导的准确率。
298 2
提升个人工作技能
提升个人工作技能
1223 6
|
人工智能 运维 监控
构建高效运维体系:理论与实践的深度融合####
本文旨在探讨高效IT运维体系的构建策略,通过理论框架与实际案例并重的方式,深入剖析了现代企业面临的运维挑战。文章开篇概述了当前运维领域的新趋势,包括自动化、智能化及DevOps文化的兴起,随后详细阐述了如何将这些先进理念融入日常运维管理中,形成一套既灵活又稳定的运维机制。特别地,文中强调了数据驱动决策的重要性,以及在快速迭代的技术环境中保持持续学习与适应的必要性。最终,通过对比分析几个典型企业的运维转型实例,提炼出可复制的成功模式,为读者提供具有实操性的指导建议。 ####
|
Dart
flutter 之 Dart 异步编程【详解】
flutter 之 Dart 异步编程【详解】
269 0
|
JavaScript Serverless 容器
ue仿携程轮播图效果(滑动轮播,下方高度自适应)
这篇文章主要介绍了vue仿携程轮播图效果(滑动轮播,下方高度自适应),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下