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

算法法导论》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 :http://www.cnblogs.com/Jiajun/archive/2013/05/15/3080111.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 个关键字?

参考:http://blog.csdn.net/minglingji/article/details/7984445

这也是“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;
	}
}

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

时间: 2024-10-12 21:01:56

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

算法导论学习---红黑树具体解释之插入(C语言实现)

前面我们学习二叉搜索树的时候发如今一些情况下其高度不是非常均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜索树,它通过在每一个结点附加颜色位和路径上的一些约束条件能够保证在最坏的情况下基本动态集合操作的时间复杂度为O(nlgn).以下会总结红黑树的性质,然后分析红黑树的插入操作,并给出一份完整代码. 先给出红黑树的结点定义: #define RED 1 #define BLACK 0 ///红黑树结点定义,与普通

算法导论学习---红黑树详解之插入(C语言实现)

前面我们学习二叉搜索树的时候发现在一些情况下其高度不是很均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜索树,它通过在每个结点附加颜色位和路径上的一些约束条件可以保证在最坏的情况下基本动态集合操作的时间复杂度为O(nlgn).下面会总结红黑树的性质,然后分析红黑树的插入操作,并给出一份完整代码. 先给出红黑树的结点定义: #define RED 1 #define BLACK 0 ///红黑树结点定义,与普通的二

【算法导论学习-016】两个已排过序的等长数组的中位数(median of two sorted arrays)

问题来源 <算法导论>P223 9.3-8: Let X[1..n] and Y[1..n] be two arrays, each containing nnumbers already in sorted order. Give an O(lgn)-time algorithm to find themedian of all 2n elements in arrays X and Y. 翻译过来即:求两个等长(n个元素)的已排序数组A和B的中位数 方案1:对两个数组进行归并直到统计到第n

【算法导论学习-015】数组中选择第i小元素(Selection in expected linear time)

1.算法思想 问题描述:从数组array中找出第i小的元素(要求array中没有重复元素的情况),这是个经典的"线性时间选择(Selection in expected linear time)"问题. 思路:算法导论215页9.2 Selection in expect linear time 2.java实现 思路:算法导论216页伪代码 /*期望为线性时间的选择算法,输入要求,array中没有重复的元素*/ public static int randomizedSelect(i

算法导论 学习资源

学习的过程会遇到些问题,发现了一些比较好的资源,每章都会看下别人写的总结,自己太懒了,先记录下别人写的吧,呵呵. 1  Tanky Woo的,每次差不多都看他的 <算法导论>学习总结 - 1.前言 <算法导论>学习总结 - 2.第一章 && 第二章 && 第三章 <算法导论>学习总结 - 3.第四章 && 第五章 <算法导论>学习总结 - 4.第六章(1) 堆排序 <算法导论>学习总结 - 5.第六

【算法导论学习-014】计数排序(CountingSortTest)

参考:<算法导论>P194页 8.2节 Counting sort 1.Counting sort的条件 待排序数全部分布在0~k之间,且k是已知数:或者分布在min~max之间,等价于分布在0~max-min之间,max和min是已知数. 2.java 实现 /** * 创建时间:2014年8月17日 下午3:22:14 项目名称:Test * * @author Cao Yanfeng * @since JDK 1.6.0_21 类说明: 计数排序法,复杂度O(n), 条件:所有数分布在0

【算法导论学习-015】基数排序(Radix sort)

1.<算法导论>P197页 8.3节Radix sort 2.java实现 这里仅仅对[算法导论学习-014]计数排序 的参数进行了修改,同时仅仅修改了一行代码. /** * 创建时间:2014年8月17日 下午4:05:48 * 项目名称:Test * @author Cao Yanfeng * @since JDK 1.6.0_21 * 类说明: 利用计数排序实现基数排序 * 条件:待排序的所有数位数相同,注意,即便不相同,也可以认为是最多那个位数,如下面的例子可以认为都是3位数 */ p

【算法导论学习笔记】第3章:函数的增长

????原创博客,转载请注明:http://www.cnblogs.com/wuwenyan/ ? 当算法的输入n非常大的时候,对于算法复杂度的分析就显得尤为重要,虽然有时我们能通过一定的方法得到较为精确的运行时间,但是很多时候,或者说绝大多数时候,我们并不值得去花精力求得多余的精度,因为精确运行时间中的倍增常量和低阶项已经被输入规模本身的影响所支配.我们需要关心的是输入规模无限增加,在极限中,运行时间是如何随着输入规模增大而增加的,通常来说,在极限情况下渐进地更优的算法在除很小的输入外的所有情

【算法导论学习-23】两个单链表(single linked)求交点

问题:A.B两个单链表如果有交点,返回第一个交点在A中的位置(链表头结点位置为0). 分析:A.B如果有交点,交点的后继一定也是交点,所以一定是Y型相交,所以算法的思想如下 1)  求得A.B的长度,比如ALength,Blength 2)  判断ALength,Blength谁大,比如Alength>Blength 3)  Alength移动到Alength-Blength的位置,开始判断每个节点是否相等,相等则退出. 以本博客中"[算法导论学习-20]单链表(single linked