问题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同样能处理,这是前面三个算法无法做到的。