百度2011.10.16校园招聘会笔试题

本文探讨了在计算机科学领域中随机算法的设计方法,包括在圆内随机生成点及在数据流中进行随机抽样的算法流程。同时,介绍了蓄水池抽样问题的解决方法及其证明过程,以及在C++中使用vector进行随机抽样的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、算法设计
1、设rand(s,t)返回[s,t]之间的随机小数,利用该函数在一个半径为R的圆内找随机n个点,并给出时间复杂度分析。


void GetNPointsInCircle(int R, int n)
{
    for (int i=0; i<n; i++)
    {
        float x = rand(-R, R);
        float y = rand(-sqrt(R*R - x*x), sqrt(R*R - x*x));
        // 也可以用极坐标表示
 
        printf("%f,%f\t", x,y);
    }
}

极坐标思路:

在半径为R的圆内找随机的n个点,既然是找点,那么就需要为其建立坐标系,如果建立平面直角坐标系,以圆心为原点,建立半径为R的圆的直角坐标系。要使随机的点在圆内,则必须使其所找的点 point 的x,y坐标的绝对值小于R,问题转化为:随机的找n个圆内的点,使其x,y坐标的绝对值均小于R。这里以x为例, 首先 x 应满足 -R<= x <= R;  y同理。这样产生的点均在以圆心为中心,边长为2R的正放形内,需要另外判断其距离R的值。本文介绍采用极坐标的形式,建立圆的极坐标系,则圆内的任意一点满足 P(ρ,θ)(用ρ表示线段OP的长度,θ表示从Ox到OP的角度angle),此时问题转化为:找一点,使其到圆心的距离OP大于等于0 小于R,极角大于等于0 小于360,则OP=rand(0, R) ,当 OP== R 时重新寻找,angle = rand(0,360) ,当angle==360时,重新寻找。每找到一个点p,将其与之前所找到的所有点进行比较,若重合,则继续寻找,否则将其加入到已找到的点集合中,直到找到n个点。

  1. 存放点的数据结构  
  2. typedef struct Point{  
  3. double r;  
  4. double angle;  
  5. }Point;  
  6. 算法流程:  
  7. for( i=n ; i >0; i--)  
  8.    产生 p.r = rand(0,R),且p.r != R  
  9.    产生 p.rangle = rand(0,360) ,且 p.rangle != 360  
  10.    遍历所有产生的点,若 p 已经存在,则重新生成该点  
  11.    否则将其加入到产生的点集合中  
存放点的数据结构
typedef struct Point{
double r;
double angle;
}Point;
算法流程:
for( i=n ; i >0; i--)
   产生 p.r = rand(0,R),且p.r != R
   产生 p.rangle = rand(0,360) ,且 p.rangle != 360
   遍历所有产生的点,若 p 已经存在,则重新生成该点
   否则将其加入到产生的点集合中

生成第n个点,需要先遍历前n-1个点,时间复杂度为O(n),故生成n个点的时间复杂度为O(n^2)


2、为分析用户行为,系统常需存储用户的一些query,但因query非常多,故系统不能全存,设系统每天只存m个query,现设计一个算法,对用户请求的query进行随机选择m个,请给一个方案,使得每个query被抽中的概率相等,并分析之,注意:不到最后一刻,并不知用户的总请求量。

蓄水池抽样问题

随机抽样问题表示如下:

要求从N个元素中随机的抽取k个元素,其中N无法确定。

这种应用的场景一般是数据流的情况下,由于数据只能被读取一次,而且数据量很大,并不能全部保存,因此数据量N是无法在抽样开始时确定的;但又要保持随机性,于是有了这个问题。所以搜索网站有时候会问这样的问题。

【解决】

解决方案就是蓄水库抽样(reservoid sampling)。主要思想就是保持一个集合(这个集合中的每个数字出现),作为蓄水池,依次遍历所有数据的时候以一定概率替换这个蓄水池中的数字。

//伪码
Init: a reservoir with the size:K
     for i= k+1 to N
  {
         M = random(1, i);
         if (M<k)
        {
     exchange(M, i);
        }
  }

解释一下:程序的开始就是把前k个元素都放到水库中,然后对之后的第i个元素,以k/i的概率替换掉这个水库中的某一个元素。

【证明】

(1)初始情况。出现在水库中的k个元素的出现概率都是一致的,都是1。这个很显然。

(2)第一步。第一步就是指,处理第k+1个元素的情况。分两种情况:元素全部都没有被替换;其中某个元素与第k+1个元素交换。

我们先看情况2:第k+1个元素被选中的概率是k/(k+1)(根据公式k/i),所以这个新元素在水库中出现的概率就一定是k/(k+1)(不管它替换掉哪个元素,反正肯定它是以这个概率出现在水库中)。下面来看水库中剩余的元素出现的概率,也就是1-P(这个元素被替换掉的概率)。水库中任意一个元素被替换掉的概率是:(k/k+1)*(1/k)=1/(k+1),意即首先要第k+1个元素被选中,然后自己在集合的k个元素中被选中。那它出现的概率就是1-1/(k+1)=k/(k+1)。可以看出来,旧元素和新元素出现的概率是相等的。

情况1:当元素全部都没有替换掉的时候,每个元素的出现概率肯定是一样的,这很显然。但具体是多少呢?就是1-P(第k+1个元素被选中)=1-k/(k+1)=1/(k+1)。

(3)归纳法:重复上面的过程,只要证明第i步到第i+1步,所有元素出现的概率是相等的即可。

1. 1)C++中vector中pushback内部是如何实现的?

Push_back函数在容器尾部创建一个新元素,并使容器长度加1。新元素为括号中元素的副本。

3、C++ STL中vector的相关问题:
    (1)、调用push_back时,其内部的内存分配是如何进行的?
    (2)、调用clear时,内部是如何具体实现的?若想将其内存释放,该如何操作?

Push_back函数在容器尾部创建一个新元素,并使容器长度加1。新元素为括号中元素的副本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值