生成长度为N的全部有序列(n-tuples)
在QQ群上和朋友聊天(嗯,我还在用QQ,尽情鄙视我吧。什么时候MSN支持像QQ那样任意添加表情,任意贴图,而不是把我添加的表情图压缩得面目全非,我再放弃QQ不迟。连“彻底地全身心地毫无保留地崇拜你”都不能用,MSN my ass。QQ上的表情:
同样的图添加到MSN后:
。什么世道!),常遇到的话题之一是怎么生成一个有序列的所有组合,一个集合的所有子集(幂集),或者所有的全排列。一些论坛上也常出现类似的问题。很有意思的话题。在编程中时不时要遇到之外,也是锻炼大脑防止老年痴呆的上佳练习材料,尤其适合好静坐,喜油条, 30岁以上从不上健身房的程序员。不用左右看了。就是老大您!做这类题目还有个好处:重温当年写小程序的快乐。不知道多少人会享受搭建工资管理系统的全过程,津津有味地调试奇形怪状的API,反复修改庞杂的XML配置文件。反正我不会。写小程序就不同了。没有最后期限的压力,不用担心系统的羁绊,无需顾虑程序的架构。可以纠缠算法的每一个细节,也可以执着于提高代码的每一分性能。施主随喜。心智澄明,目光通透,心随意动,运指如飞。敲下的字符直切问题要害,摧枯拉朽。层层屏障随着代码的延伸支离破碎。写小程序解谜题的过程,就好像懵懂小孩儿扎堆游戏,纯粹为了好玩儿。一晃眼,一下午过去。他们满身泥浆,精疲力尽。但他们眼神依然兴奋,依然盼望下一次游戏。嗯,今年不收礼,收礼只收智力题。
今天又开始犯贱,万事压身就是不想动手。不过秉承拖拉也要拖有所得的原则,干脆聊聊这类问题的常用算法。
知道怎么生成全部有序列的算法,也就知道怎么生成幂集。这两者有直观的对应关系。生成全排列则另有一套算法。生成所有有序列要简单些,所以先用它开胃。当然,这类问题早有
大牛写了详尽的
指南, 我也就当当廉价的搬运工而已。好在还能用大牛本人的话安慰一下自己:每当发现什么有趣的问题后,轻轻Google一把,总能不幸发现有聪明人已经做出答案。
一个N-tuple是一个包含N个元素的序列,一般写作
。比如说
就是一个6-tuple。注意tuple和集合的区别。Tuple里可以包含重复的元素。而且Tuple的元素是有序的,就跟数组的元素一样。
那么给定一个N-tuple,
,我们怎么生成全部有序列呢?由简入繁总是学习的不二法门。所以我们从简单的二进制N元有序列(binary n-tuple)开始:二进制N元有序列的每个元素都是一个bit,非0即1。比如说一个二进制3-tuple,生成的全部有序列为
(0, 0, 0)
(0, 0, 1)
(0, 1, 0)
(0, 1, 1)
(1, 0, 0)
(1, 0, 1)
(1, 1, 1)
一共8个。二进制N元有序列虽然简单,却已经有了广泛的用途。比如说知道怎么生成二进制N元有序列,我们也就知道怎么生成幂集:一个N元集合
,我们规定
属于于一个子集,当且仅当对应的二进制N元有序列的元素
。用上面的3-tuple作例子。给出一个集合
,则
下面还会讨论更多的应用。而且从讨论二进制N元有序列中得出的结论为我们以后探讨怎么生成更复杂的组合样式奠定基础。
生成全部二进制N元有序列的方法再简单不过:我们从二进制数
开始,逐次加一,直到得到
为止。利用进位加法,我们刚好遍历了所有的N-tuples。简单,但是美妙。代码用Ruby实现的。因为Ruby的代码和伪代码差别不大,会不会Ruby的老大都可以