您的位置:首页 > 其它

【算法导论学习-012】n个数随机等概率的抽样m个

2014-08-13 15:53 225 查看

算法法导论》P129页课后题5.3-7

suppose we want to create a random sample of the set {1,2,3,…,n}, thatis, an m-element subset S, where0≤m≤n, such that each m-subset is equally likely to be created. One waywould be to set A[i]=i for i=1,2,3,…,n, call RANDOMIZE-IN-PLACE(A),
and then take just the first marray elements. This method would make n calls to the RANDOM procedure.
If n is much larger than m, we can create a random samplewith fewer calls to RANDOM. Show that the following recursive procedurereturns a random m-subset S of {1,2,…,n}, in which eachm-subset is equally likely, while making
only m calls to RANDOM:

RANDOM-SAMPLE(m,n)
if m == 0
return ∅
else
S = RANDOM-SAMPLE(m-1, n-1)
i = RANDOM(1,n)
if i ∈ S
S = S ∪ {n}
else
S = S ∪ {i}
return S


翻译过来就是:n个数随机等概率的取样m个。

该题的证明方法1:http://clrs.skanev.com/05/03/07.html

该题的证明方法2 :/article/6972015.html

题目中其实给出了两种解决方案。

方案1:调用RANDOMIZE-IN-PLACE(A)

/**
* 创建时间:2014年8月13日 上午9:46:51
* 项目名称:Test
* @author Cao Yanfeng
* @since JDK 1.6.0_21
* 类说明:
*/
public class RandomSampleTest {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int[] result=randomSample(array, 5);
for (int i : result) {
System.out.println(i);
}

}
public static int[] randomSample(int[] array,int m) {
randomInPlace(array);
int[] result=new int[m];
for (int i = 0; i <m; i++) {
result[i]=array[i];
}
return result;

}
/*《算法导论》P126页伪代码*/
public static void randomInPlace(int[] array) {
int n=array.length;
for (int i = 0; i < n; i++) {
int index=random(i, n-1);
if (array[i]!=array[index]) {
array[i]^=array[index];
array[index]^=array[i];
array[i]^=array[index];
}
}

}
public static int random(int a,int b) {
return new Random().nextInt(b-a+1)+a;
}
}


方案2:实现题目中的伪代码

/**
* 创建时间:2014年8月13日上午9:46:51

* 项目名称:Test

* @author
Cao Yanfeng
* @since JDK 1.6.0_21

* 类说明:

*/
public
class
RandomSampleTest {

/**
* @param args
*/
public
static void
main(String[]
args) {
// TODO Auto-generated method stub
int[]
array={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
LinkedList<Integer>result=randomSample(array, 5);
for (Integer
integer : result) {
System.out.println(integer);
}
}

public
static
LinkedList<Integer> randomSample(int[]
array,int
m){
return
sample(array,
array.length,
m);

}
public
static
LinkedList<Integer> sample(int[]
array,int
n,int
m) {
if (m==0) {
return
new
LinkedList<Integer>();
}else {
LinkedList<Integer> s=sample(array,
n-1, m-1);
int
i=array[random( 0,
n-1)];
if (s.contains(i)) {
s.add(array[n-1]);
}else {
s.add(i);
}
return
s;
}
}
/*返回闭区间的[a,b]随机数*/
public
staticint
random(inta,int
b) {
return
new
Random().nextInt(b-a+1)+a;
}
}

方案3:赋予权重法

《算法导论》P1225.3节的Randomized algorithms中提供的第一种随机采样方法即赋予权重法。但是权重有可能出现相同情况,不推荐这种方法。

方案4:蓄水池抽样

见最后的扩展问题。
*******************************************************************************
正如题目中所言,如果n个数据选取m个样本,如果n远大于m,则应该使用方案2,仅调用m次random()函数;如果n与m差距不大,则应该使用方案1,调用n次random()函数,但是方法简单。
*******************************************************************************
扩展问题:【google面试题】给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字) 。如何才能从这个无穷尽的流中随机的选取 1000 个关键字?
参考:/article/9402058.html
这也是“n个数随机等概率的取样m个”问题,但是n是未知的。采用的方式是蓄水池抽样。即:将数据流中的前1000个放入长度为1000的数组,对于1001个数,调用random(0,
1000),[0,999]闭区间内每个数被选中的概率都是1000/1001。之后对于n>1000的每个数,[0,999]闭区间内每个数被选中的概率都是1000/n。这里random被调用的次数为n-m。下面模拟一下这个过程。
/**
* 创建时间:2014年8月13日 上午9:46:51
* 项目名称:Test
* @author Cao Yanfeng
* @since JDK 1.6.0_21
* 类说明:
*/
public class RandomSampleTest {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int[] result=reservoirSample(array, 5);
for (int i : result) {
System.out.println(i);
}
}
/*蓄水池抽样*/
public static int[] reservoirSample(int[] array,int m) {
int[] reservoir=new int[m];
for (int i = 0; i <array.length; i++) {
if (i<m) {
reservoir[i]=array[i];
}else {
int temp=random(0, i);
if (temp<m) {
reservoir[temp]=array[i];
}
}

}
return reservoir;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: