排列、组合、蓄水池抽样

排列

从{1,2,3,4,5} 5个元素中取3个数排列。第一次取有5种情况,拿出一个数后,第二次取有4种情况,第三次取有3中情况。所以5中取3的排列共有
5*(5-1)(5-2) = 60种情况,由{1,2,3} 3个数的排列由32*1=6中。也就是每一种组合有6种排列。

组合

所以{1,2,3,4,5}中取三个数的组合为
5 ∗ ( 5 − 1 ) ∗ ( 5 − 2 ) 3 ∗ ( 3 − 1 ) ( 3 − 2 ) = 10 \frac{5*(5-1)*(5-2)}{3*(3-1)(3-2)} = 10 3(31)(32)5(51)(52)=10
推广到一般情况,从N个元素中取K个元素的组合个数,记作C(N,K),则有
C ( N , K ) = N ∗ ( N − 1 ) ∗ ( N − 2 ) . . . ∗ ( N − ( K − 2 ) ) ∗ ( N − ( K − 1 ) ) K ∗ ( K − 1 ) ∗ ( K − 2 ) . . . ∗ 3 ∗ 2 ∗ 1 C(N,K) = \frac{N*(N-1)*(N-2)...*(N-(K-2))*(N-(K-1))}{K*(K-1)*(K-2)...*3*2*1} C(NK)=K(K1)(K2)...321N(N1)(N2)...(N(K2))(N(K1))

其中,含有某个元素m的组合个数,取出m后,还剩N-1个元素,从中要抽取另外K-1个元素,和m组成组合。所以C(N,K)个组合中含有某个确定元素m的组合个数为C(N-1,K-1),那么对于N个元素取出K个元素,某个元素被抽中的概率
P = C ( N − 1 , K − 1 ) C ( N , K ) = K N P = \frac{C(N-1,K-1)}{C(N,K)} = \frac{K}{N} P=C(N,K)C(N1,K1)=NK

蓄水池抽样

123456KK+1N-1N

使用均匀随机数生成函数rand(),从N个元素中抽取K个元素。要求每个元素被抽中的概率符合 K N \frac{K}{N} NK
为了便于理解,下面的说明违背了程序编写的传统,数组索引不从0开始,而从1起始。
从第K+1个元素开始处理,

第K+1个元素,int index = rand()%(K+1) + 1; index的取值范围是[1,K+1],如果 index 的取值在[1,K]上,则 把第K+1个元素与index位置的元素交换。这个事件发生的概率为 K K + 1 \frac{K}{K+1} K+1K
第K+2个元素,int index = rand()%(K+2) + 1; index的取值范围是[1,K+2],如果 index 的取值在[1,K]上,则 把第K+2个元素与index位置的元素交换。这个事件发生的概率为 K K + 2 \frac{K}{K+2} K+2K
第K+3个元素,int index = rand()%(K+3) + 1; index的取值范围是[1,K+3],如果 index 的取值在[1,K]上,则 把第K+3个元素与index位置的元素交换。这个事件发生的概率为 K K + 3 \frac{K}{K+3} K+3K


第N-1个元素,int index = rand()%(N-1) + 1; index的取值范围是[1,N-1],如果 index 的取值在[1,K]上,则 把第N-1个元素与index位置的元素交换。这个事件发生的概率为 K N − 1 \frac{K}{N-1} N1K
第N个元素,int index = rand()%(N) + 1; index的取值范围是[1,N],如果 index 的取值在[1,K]上,则 把第N个元素与index位置的元素交换。这个事件发生的概率为 K N \frac{K}{N} NK

index123456KK+1N-1N
p K K + 1 \frac{K}{K+1} K+1K K N − 1 \frac{K}{N-1} N1K K N − 2 \frac{K}{N-2} N2K

证明,经过这轮操作后,N个元素中每个元素在前K个的概率均符合要求,分两种情况讨论。
1,索引为m的元素,m<=K,想要在处理后仍然在前K个,必须满足,所有的随机生成int index全不为m,淘汰是个单向过程,一旦被从前K个剔除,就无法回到前K个中。所以
P = K K + 1 ∗ K + 1 K + 2 ∗ K + 2 K + 3 ∗ N − 2 N − 1 ∗ N − 1 N = K N P = \frac{K}{K+1} *\frac{K+1}{K+2} *\frac{K+2}{K+3} *\frac{N-2}{N-1} * \frac{N-1}{N} = \frac{K}{N} P=K+1KK+2K+1K+3K+2N1N2NN1=NK

2,索引为m的元素,m>K,在处理前不在前K个元素,所以它被抽中必须要满足处理到它时的计算出的index值小于等于K,这样m元素就被交换到了前K个,且在处理后面的元素是,m元素没被交换出去。
P = K m ∗ m m + 1 ∗ m + 1 m + 2 ∗ N − 2 N − 1 ∗ N − 1 N = K N P = \frac{K}{m} *\frac{m}{m+1} *\frac{m+1}{m+2} *\frac{N-2}{N-1} * \frac{N-1}{N} = \frac{K}{N} P=mKm+1mm+2m+1N1N2NN1=NK
证的,N中某个任意元素经过这个操作,位置在K前个的概率等于,N个元素取出K个元素,某个元素被抽中的概率。

#include <vector>
#include <iostream>
using namespace std;

const int N = 1000;
const int K = 100;
int main(int argc, char *argv[])
{
    vector<int> v;
    for(int i=0;i<N;i++)
    {
        v.push_back(i);
    }
    for(int i=K;i<N;i++)
    {
        int index = rand()%(i+1);
        if(index<K)
        {
            int temp = v[index];
            v[index] = v[i];
            v[i] = temp;

//            cout<<index<<"  "<< v[index] << " "<<v[i]<<endl;
        }

    }
    for(int i=0;i<K;i++)
    {
        cout<<v[i]<<endl;
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值