隨機讀取數據,如何保證真隨機是不可能的,因為計算機的隨機函數是偽隨機的。
但是在不考慮計算機隨機函數的情況下,如何保證數據的隨機采樣呢?
1.系統提供的shuffle函數
C++/Java都提供有shuffle函數,可以對容器內部的數據打亂,保持隨機排序。
C++:
1 template <class RandomAccessIterator, class URNG> 2 void shuffle (RandomAccessIterator first, RandomAccessIterator last, URNG&& g);
Java:
1 static void shuffle(List<?> list); 2 static void shuffle(List<?> list, Random rnd);
這些函數對數量一定的數據的隨機打亂順序,並不能處理數量不定的數據流。
2.在序列流中取一個數,如何確保隨機性,即取出某個數據的概率為:1/(已讀取數據個數)
假設已經讀取n個數,現在保留的數是Ax,取到Ax的概率為(1/n)。
對於第n+1個數An+1,以1/(n+1)的概率取An+1,否則仍然取Ax。依次類推,可以保證取到數據的隨機性。
數學歸納法證明如下:
當n=1時,顯然,取A1。取A1的概率為1/1。
假設當n=k時,取到的數據Ax。取Ax的概率為1/k。
當n=k+1時,以1/(k+1)的概率取An+1,否則仍然取Ax。
(1)如果取Ak+1,則概率為1/(k+1);
(2)如果仍然取Ax,則概率為(1/k)*(k/(k+1))=1/(k+1)
所以,對於之后的第n+1個數An+1,以1/(n+1)的概率取An+1,否則仍然取Ax。依次類推,可以保證取到數據的隨機性。
代碼如下:
1 //在序列流中取一個數,保證均勻,即取出數據的概率為:1/(已讀取數據個數) 2 void RandNum(){ 3 int res=0; 4 int num=0; 5 num=1; 6 cin>>res; 7 8 int tmp; 9 while(cin>>tmp){ 10 if(rand()%(num+1)+1>num) 11 res=tmp; 12 num++; 13 } 14 cout<<"res="<<res<<endl; 15 }
3.在序列流中取k個數,如何確保隨機性,即取出某個數據的概率為:k/(已讀取數據個數)
建立一個數組,將序列流里的前k個數,保存在數組中。(也就是所謂的"蓄水池")
對於第n個數An,以k/n的概率取An並以1/k的概率隨機替換“蓄水池”中的某個元素;否則“蓄水池”數組不變。依次類推,可以保證取到數據的隨機性。
數學歸納法證明如下:
當n=k是,顯然“蓄水池”中任何一個數都滿足,保留這個數的概率為k/k。
假設當n=m(m>k)時,“蓄水池”中任何一個數都滿足,保留這個數的概率為k/m。
當n=m+1時,以k/(m+1)的概率取An,並以1/k的概率,隨機替換“蓄水池”中的某個元素,否則“蓄水池”數組不變。則數組中保留下來的數的概率為:
所以,對於第n個數An,以k/n的概率取An並以1/k的概率隨機替換“蓄水池”中的某個元素;否則“蓄水池”數組不變。依次類推,可以保證取到數據的隨機性。
代碼如下:
1 //在序列流中取n個數,保證均勻,即取出數據的概率為:n/(已讀取數據個數) 2 void RandKNum(int n){ 3 int *myarray=new int[n]; 4 for(int i=0;i<n;i++) 5 cin>>myarray[i]; 6 7 int tmp=0; 8 int num=n; 9 while(cin>>tmp){ 10 if(rand()%(num+1)+1<n) 11 myarray[rand()%n]=tmp; 12 } 13 14 for(int i=0;i<n;i++) 15 cout<<myarray[i]<<endl; 16 }