编程珠玑12章习题 12.9 FLoyd随机取样算法的证明(转自编程珠玑续)
2011-12-14 13:51
429 查看
13.3 随机排列
一些使用随机样本的程序要求样本的元素以随机的顺序出现。这样的序列被称为无重复的随机排列。例如,在测试一个排序程序的时候,随机产生的输入必须以随机的顺序出现;如果输入总是有序的,那么可能不能充分地测试排序代码。
我们可以利用Floyd算法F2产生一组随机样本,然后把它复制到一个数组中,最后打乱数组中元素的顺序。这段代码用于随机地打乱数组 的顺序:
这个只有三个步骤的方法调用了RandInt函数2M次。
当本章原来在《ACM通讯》上发表后,几位读者发现上面的伪代码经过小的修改后,能够从1..N的整数中产生M元随机排列并放入X[1..M]中:
这个算法很容易实现成代码,但是它需要O(N)的运行时间和O(N)的空间。下面我们会看到,Floyd的算法在N相对于M比较大的时候,相比之下会更有效率。
Floyd的随机排列产生器与他的算法F2类似。为了产生1~N内的一组M元排列,它会先从1~N 1中产生一组M 1元的排列。(算法的递归版本中没有变量J。)但是,排列产生器的主要数据结构是序列而非集合。下面是Floyd的算法P。
算法P
从习题5可以看出,算法P在随机位的使用上尤其高效。习题6讨论了序列S的高效率实现。
我们可以从算法在M=N时的行为得到关于算法P的直观的感觉,此时算法生成N元的随机排列,其中J从1到N循环。在执行循环体之前,S是一个1~J 1的整数的随机排列。循环体把J插入到序列中仍然保持了这一点;当T=J时,J成为第一个元素,否则J被随机放置于已经存在的J 1个元素的某一个之后。
一般地,算法P以等概率生成1~N内的每一个M元排列。Floyd对于正确性的证明用到循环不变式:第i轮循环后,J=N M+i且S可能是1~J中i个不同整数的任意排列,并且只有一种途径可以生成这个排列。
Doug McIlroy发现了一种优雅的方式来说明Floyd的证明:对于任何一个排列,有且仅有一种途径来生成它,因为算法是可以逆推的。例如,假设M=5,N=10,且最终的序列为
由于10(J的最终取值)不在S中出现,所以之前的序列肯定是
且RandInt返回值为T=7。又因为9(相应的J的值)出现在4元序列中的2之后,所以之前的T是2。习题4说明了可以类似地恢复出整个随机数序列。由于假定了所有的随机序列是以相同的可能性出现的,于是所有的排列也同样是等概率的。
我们现在可以利用与算法P的相似性来证明算法F2。在算法的每一步,算法F2中的集合S和算法P中的序列S所含的元素是相同的。因此,1~N的每一个M元子集都由M!个随机序列生成,于是它们是等概率的。
一些使用随机样本的程序要求样本的元素以随机的顺序出现。这样的序列被称为无重复的随机排列。例如,在测试一个排序程序的时候,随机产生的输入必须以随机的顺序出现;如果输入总是有序的,那么可能不能充分地测试排序代码。
我们可以利用Floyd算法F2产生一组随机样本,然后把它复制到一个数组中,最后打乱数组中元素的顺序。这段代码用于随机地打乱数组 的顺序:
for I := M downto 2 do J := RandInt(1, I) Swap(X[J], X[I])
这个只有三个步骤的方法调用了RandInt函数2M次。
当本章原来在《ACM通讯》上发表后,几位读者发现上面的伪代码经过小的修改后,能够从1..N的整数中产生M元随机排列并放入X[1..M]中:
for I := 1 to N do X[I] := I for I := 1 to M do J := RandInt(I, N) Swap(X[J], X[I])
这个算法很容易实现成代码,但是它需要O(N)的运行时间和O(N)的空间。下面我们会看到,Floyd的算法在N相对于M比较大的时候,相比之下会更有效率。
Floyd的随机排列产生器与他的算法F2类似。为了产生1~N内的一组M元排列,它会先从1~N 1中产生一组M 1元的排列。(算法的递归版本中没有变量J。)但是,排列产生器的主要数据结构是序列而非集合。下面是Floyd的算法P。
算法P
initialize sequence S to empty for J := N - M + 1 to N do T = RandInt(1, J) if T is not in S then prefix T to S else insert J in S after T
从习题5可以看出,算法P在随机位的使用上尤其高效。习题6讨论了序列S的高效率实现。
我们可以从算法在M=N时的行为得到关于算法P的直观的感觉,此时算法生成N元的随机排列,其中J从1到N循环。在执行循环体之前,S是一个1~J 1的整数的随机排列。循环体把J插入到序列中仍然保持了这一点;当T=J时,J成为第一个元素,否则J被随机放置于已经存在的J 1个元素的某一个之后。
一般地,算法P以等概率生成1~N内的每一个M元排列。Floyd对于正确性的证明用到循环不变式:第i轮循环后,J=N M+i且S可能是1~J中i个不同整数的任意排列,并且只有一种途径可以生成这个排列。
Doug McIlroy发现了一种优雅的方式来说明Floyd的证明:对于任何一个排列,有且仅有一种途径来生成它,因为算法是可以逆推的。例如,假设M=5,N=10,且最终的序列为
7 2 9 1 5
由于10(J的最终取值)不在S中出现,所以之前的序列肯定是
2 9 1 5
且RandInt返回值为T=7。又因为9(相应的J的值)出现在4元序列中的2之后,所以之前的T是2。习题4说明了可以类似地恢复出整个随机数序列。由于假定了所有的随机序列是以相同的可能性出现的,于是所有的排列也同样是等概率的。
我们现在可以利用与算法P的相似性来证明算法F2。在算法的每一步,算法F2中的集合S和算法P中的序列S所含的元素是相同的。因此,1~N的每一个M元子集都由M!个随机序列生成,于是它们是等概率的。
相关文章推荐
- 编程珠玑: 12章 取样问题 12.1程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。 优化解法-------解题总结
- 编程珠玑之第二章习题6
- 编程珠玑之第一章习题4: 生成不同的随机顺序的随机整数及存储与读取
- 编程珠玑之第一章习题3:度量100w整数排序的运行时间
- 编程珠玑 第十三章 习题4 思考
- 编程珠玑 第二章 习题2
- 编程珠玑之第二章习题2
- 编程珠玑 第二章 习题6~9
- 编程珠玑: 12章 取样问题 12.3设计空间,程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。 -------解题总结
- 编程珠玑第三章—习题4(日期问题)
- 编程珠玑之第12章习题1: 随机位问题
- 编程珠玑 第一章习题解答
- 编程珠玑 第二章 习题5
- 编程珠玑: 12章 取样问题 12.3设计空间,程序的输入包含两个整数m和n,其中m<n。输出是0~n-1范围内m个随机整数的有序列表,不允许重复。 解法2-------解题总结
- 编程珠玑习题:多趟位图排序,数字不重复出现
- 编程珠玑之第三章习题5
- 编程珠玑之第二章习题4
- 由July师兄二分查找代码及编程珠玑有感:循环不变性(断言)证明程序的正确性及发现bug
- 编程珠玑之第三章习题3
- 编程珠玑第五章习题五——C++实现二分搜索时进行错误检测