等機率抽樣Floyd演算法

來源:互聯網
上載者:User

標籤:style   os   io   strong   for   cti   代碼   amp   line   

對於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);elseq.insert(t_position+1,1,j+1);}for(deque<int>::iterator iter=q.begin();iter!=q.end();++iter)printf("%-3d",*iter);cout<<endl;}

等機率抽樣Floyd演算法(轉)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.