問題1:寫一個隨機洗牌函數。要求洗出的所有組合都是等概率的。
這裏我們的答案:都假定數組從0開始:
1. 參考Cracking the coding interview--答案20.2
void RandomShuffle1(int a[], int n){
for(int i=0; i<n-1; ++i){
int j = rand() % (n-i) + i; // 產生i到n-1間的隨機數
Swap(a[i], a[j]);
}
}
2. STL中用的random_shuffle算法:參考侯捷STL源碼剖析
void RandomShuffle2(int a[], int n){
for(int i=1; i<n; ++i){
int j = rand() % (i+1) ; // 產生0到i間的隨機數
Swap(a[i], a[j]);
}
}
3. 從尾到頭類似2:
void RandomShuffle3(int a[], int n){
for (int i=n-1; i>0; --i)
{
int j = rand()%(i+1); // 產生0到i間的隨機數
swap(a[j],a[j]);
}
}
4 從尾到頭類似1
void RandomShuffle4(int a[], int n){
for (int i=n-2; i>=0; --i)
{
int j = rand() % (n-i) + i; // 產生i到n-1間的隨機數
swap(a[j],a[j]);
}
}
可以考慮爲什麼STL中選取的是第二種方法呢?
問題2:隨機地從大小爲n的數組中選取m個元素出來,然後輸出。要求每個元素被選中的概率都相等。
假設有一個5維數組:1,2,3,4,5。如果第1次隨機取到的數是4, 那麼我們希望參與第2次隨機選取的只有1,2,3,5。既然4已經不用, 我們可以把它和1交換,第2次就只需要從後面4位(2,3,1,5)中隨機選取即可。同理,
第2次隨機選取的元素和數組中第2個元素交換,然後再從後面3個元素中隨機選取元素, 依次類推。
1. 參見編程珠璣
void getRandNumber1(int A[], int m, int n)
{
srand(time(NULL));
int i;
for(i=0;i<n;++i)
{
if( rand()%(n-i) < m)
{
printf("%d ",A[i]);
m--;
}
}
}
2. 利用set中的元素不重複性:參見編程珠璣
void getRandNumber2(int A[], int m,int n)
{
srand(time(NULL));
set<int> S;
while(S.size()<m) //直到填滿
S.insert(A[rand()%n]);
set<int>::iterator i;
for(i=S.begin();i!=S.end();++i)
cout<<*i<<" ";
}
當m接近n時,該算法要丟掉很多隨機數,因爲它們之前已經在集合中,故修改成下面算法,最壞情況下也只用m個隨機數。
Floyed 基於集合的算法:參考編程珠璣
void getSet(int A[], int m,int n)
{
srand(time(NULL));
set<int> S;
for(int i=n-m;i<n;++i)
{
int t=rand()%(i+1);
if(S.find(t) == S.end())
S.insert(A[t]);
else
S.insert(A[i]);
}
set<int>::iterator j;
for(j=S.begin();j!=S.end();++j)
cout<<*j<<" ";
}
3. 參考Cracking the coding interview--答案20.3
void getRandNumber3(int A[], int m, int n)
{
srand(time(NULL));
for(int i=0;i<m;++i)
{
int j = rand()%(n-i)+i; //產生i-n-1的隨機數
swap(A[i],A[j]);
}
for(i=0;i<m;++i)
cout<<A[i]<<" ";
}
4. 蓄水池抽樣算法:
void getRandNumber4(int A[], int m, int n)
{
srand(time(NULL));
for(int i=m;i<n;++i)
{
int j = rand()%(i+1) //產生0-i的隨機數
if( j<m)
swap(A[i],A[j]);
}
for(i=0;i<m;++i)
cout<<A[i]<<" ";
}
先把前m個元素放入蓄水池,對第m+1個元素,我們以m/(m+1)概率決定是否要把它換入蓄水池,換入時隨機的選取一個作爲替換項,這樣一直做下去,對於任意的樣本空間n,對每個元素的選取概率都爲m/n,也就是說對每個元素選取概率相等。
當n未知的時候,算法4同樣能處理,這是前面三個算法無法做到的。