等概率抽样Floyd算法(转)

对于n个样本,如何均匀随机的取出m个样本?即n个样本中每个样本都能有m/n的概率被取中。

1.简单插入取样

这是最基本,最直观的方法。在一个初始为空的集合中插入1~n的随机整数,知道个数为m个为止。但这个方法有个弱点,就是要插入一个数时,判断集合中是否存在该数,如果其存在,则要继续取样直到取到一个不在原集合中的数,重复取样需要很大的开销,而且越到后来开销越大。

2.Floyd取样(将第n个数插入与前n-1个数的样本的选取关联起来)

Floyd取样是大名鼎鼎的Robert W. Floyd提出来的。其基本思想:当已经在[1,n-1]个区间中已随机取出m-1个样本时,这时,生成一个1~n的随机数,如果该数落在原来的m-1个样本中,则样本集合加入n;如果不落在原来的m-1个样本中,那么就将随机数加入已取样本集合。这样的算法复杂度为O(m)

证明:对于第n个数,其被选中的概率为1/n+(m-1)/n=m/n;

而对于前面n-1个数中的任意一个数,总被选中的概率P=上一轮被选中的概率+上一轮未被选中的概率*本轮被选中的总概率:

易得P=(m-1)/(n-1)*1+(n-m)/(n-1)*1/(n+1)=m/n.

递归实现:

  1. Function Sample(M,N)  //编程珠玑第13章Floyd算法
  2.      if M = 0 then
  3.       return the empty set;
  4.      else
  5.       S := Sample(M-1,N-1)
  6.       T := RandInt(1,N)
  7.       if T is not in S then
  8.         insert T in S
  9.       else
  10.         insert N in S
  11.       return
  12.  则把N插入到S中的概率为(M-1+1)/N = M/N;
  1. void floySampling(int n,int m) { if(m>n){ printf("Error\n"); return; } vector<int> q; for(int j=n-m;j<n;++j){ int t=rand()%(j+1); ++t; vector<int>::iterator t_position=find(q.begin(),q.end(),t); if(t_position==q.end()) q.push_back(t); else q.push_back(j+1); } for(vector<int>::iterator iter=q.begin();iter!=q.end();++iter) printf("%-3d",*iter); cout<<endl; }

3.有百度一到笔试题所联想到的取样方法

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

答案策略:取一个[1,m+i]中的随机数,如果随机数落在(m,m+i]时,应该保留原来的m个数;如果随机数落在[1,m]中,则应该用最新的一条记录取代[1,m]中随机的一个数。
证明如下:
1)假设现在系统读取第n+1条记录,现在存储的m条记录都是前面m+n条记录中以m/(m+n)的概率留下来的;
2)取一个[1,m+n+1]的随机数,按照上述策略。
3)现在新记录能保留在m数组的概率为m/(m+n+1)
4)原来m数组中的数(设为A)在本轮选择中还能保留的条件概率(条件是,上一轮选择中,A被保留):
      (n+1)/(m+n+1)+m/(m+n+1)*(1-1/m)=(m+n)/(m+n+1)。
      然后要乘以其原来保留下的概率。得到的A仍在m数组中的概率为m/(m+n+1)。

如此循环,总是可以保障每个数被选择的概率相等。该算法的复杂度为O(n)

  1. void baiduSampling(int n,int m)
    {
    if(m>n){
    printf("Error\n");
    return;
    }

    vector<int> v;
    for(int i=1;i<=m;++i){
    v.push_back(i);
    }
    for(int i=m+1;i<=n;++i){
    int t=rand()%(i-1);
    ++t;
    if(t<=m)
    v[t-1]=i;
    }

    for(vector<int>::iterator iter=v.begin();iter!=v.end();++iter)
    printf("%-3d",*iter);
    cout<<endl;

    }

4.随机顺序序列

有时,我们不仅希望能在n个样本中随机选取m个样本,还希望m个样本的顺序也是随机的。显然,简单插入取样最后得到的序列顺序是随机的。而后面两种方法得到的顺序并不是随机的。Floyd每一轮插入样本中,如果随机数在原来的集合中,最大的数总是被插在容器的最后面,这是造成顺序不随机的主要原因。我们令其插入在随机数的后面。以上只是直观的上的说明。真正严谨的证明请看编程珠玑2的13章。

第三种方法的不随机主要是因为初始的前m个样本序列的不随机造成的。

稍微修改一下floyd算法代码即可得到能生成随机顺序的序列的算法:

  1. void generateRandomSeries(int n,int m)
    {
    if(m>n){
    printf("Error\n");
    return;
    }

    deque<int> q;
    for(int j=n-m;j<n;++j){
    int t=rand()%(j+1);
    ++t;
    deque<int>::iterator t_position=find(q.begin(),q.end(),t);
    if(t_position==q.end())
    q.push_front(t);
    else
    q.insert(t_position+1,1,j+1);
    }

    for(deque<int>::iterator iter=q.begin();iter!=q.end();++iter)
    printf("%-3d",*iter);
    cout<<endl;
    }

时间: 2024-10-23 03:34:43

等概率抽样Floyd算法(转)的相关文章

ACM: POJ 3660 Cow Contest - Floyd算法

链接 Cow Contest Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Description N (1 ≤ N ≤ 100) cows, conveniently numbered 1..N, are participating in a programming contest. As we all know, some cows code better than others. Eac

最短路径Dijkstra算法和Floyd算法整理、

转载自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 最短路径—Dijkstra算法和Floyd算法 Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹

最短路径——Floyd算法

如何求一张图中任意两顶点之间的最短路径长度,这里写一种最简单的算法——Floyd算法: 1 #include<stdio.h> 2 3 #define inf 9999 4 5 int main() 6 { 7 int e[10][10]; //用邻接矩阵表示图 8 printf("请输入顶点和边的数目:"); 9 int n,m; 10 scanf("%d%d",&n,&m); 11 for(int i=0;i<n;i++) 12

HDOJ 1217 Arbitrage(拟最短路,floyd算法)

Arbitrage Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 5679    Accepted Submission(s): 2630 Problem Description Arbitrage is the use of discrepancies in currency exchange rates to transform

44. 蛤蟆的数据结构笔记之四十四弗洛伊德Floyd算法

44. 蛤蟆的数据结构笔记之四十四弗洛伊德Floyd算法 本篇名言:"希望是厄运的忠实的姐妹. --普希金" 我们继续来看下数据结构图中的一个算法,这个算法来自图灵奖得主. 1.  Floyd算法介绍 Floyd算法又称为插点法,是一种用于寻找给定的加权图中多源点之间最短路径的算法.该算法名称以创始人之一.1978年图灵奖获得者.斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名.注意这个可不是心理学的那个弗洛伊德. 是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径

所有顶点之间的最短路径算法:Floyd算法。

Floyd算法的基本思想是:设集合S的初始状态为空,然后依次向集合S中加入顶点 0,1,...,n-1,每次加入一个顶点,用二维数组d保存各条最短路径的长度,其中d[i][j]存放的是顶点i到顶点j的最短路径的长度. 详细的说明: Floyd算法中最重要的办法为二维数组d[i][j],d[i][j]为从i到j中间只经过S中的顶点的.所有可能的路径中的最短路径的长度.如果从i到j通过S中的节点无法联通,则设置d[i][j]为正无穷.可以称d[i][j]存放的是从i到j的 当前最短路径 的长度.而随

最短路径之Floyd算法

Floyd算法又称弗洛伊德算法,也叫做Floyd's algorithm,Roy–Warshall algorithm,Roy–Floyd algorithm, WFI algorithm. Floyd算法是一种在有权图中(有确定的非负的权值,不能存在环路)查找最短路径的算法.该算法的一次简单执行可以找出任意结点之间的最短路径(尽管它没有返回路径的具体信息). 思想: Floyd算法通过比较图中任意两点间所有可能存在的路径长度得到最短路径长度. 我们定义一个函数shortestPath(i,j,

hihoCoder#1089 最短路径&#183;二:Floyd算法

原题地址 感觉Floyd算法比Dijkstra还要简单.. 唯一需要注意的是,初始的距离默认值不要设过大,否则溢出就不好了,根据题意,只要大于10^3即可 代码: 1 #include <iostream> 2 #include <cstring> 3 4 using namespace std; 5 6 #define MAX_POINT 1024 7 #define MAX_EDGE 16384 8 9 int N, M; 10 int d[MAX_POINT][MAX_POI

hihocoder1089 Floyd算法

题目链接:http://hihocoder.com/problemset/problem/1089 算法描述: floyd算法是求解图中任意两点最短路的经典算法,复杂度为O(n^3).虽然我们完全可以用n次dijkstra算法来求任意两点的最短路,复杂度也是O(N^3),但如果有一个算法只需要5行代码就可以完成我们想做的事情,我们有什么理由不用它?! floyd算法主要用了动态规划的思想: 设d[i][j]表示i到j的最短路径的长度, 那么我们可以这样来求解d[i][j]: (1)首先,i到j的