1、遍历组合数的方法原理
组合数是高中代数的知识,这里我就不废话了。
这里讨论的是如何遍历组合数。举个例子,一个电影院里面,有十个座位和8个客人,到底有多少种坐法?(不考虑客人之间的相互位置。)高中组合数的知识告诉我们答案是C = 10 * 9 / 1 * 2 = 45种。
多少种坐法现在我们知道了,现在我们来考虑另外一个变态一点的问题,能不能把45种坐法都罗列出来。
这里给每个座位都编个号(假设第一个座位编号为0),这样就可以列举一些可能的坐法出来:
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7 9
0 1 2 3 4 5 6 8 9
0 1 2 3 4 5 7 8 9
0 1 2 3 4 6 7 8 9
............
上面的例子来说,对于一般人来说可能还不是很困难,反正也就是45个而已嘛,一个个数总可以数出来。但如果改变题法,比如,50个座位坐5个客人,这样的组合数就达到百万级了,这样的任务对于人来说就已经很困难。
因为组合数的增长速度近似于是指数级的增长,所以靠人肉眼去数是绝对数不过来的,那靠计算机能否实现呢?当然能。不过这需要用到一些数学上面的小技巧。下面的定理给出了这种遍历方法的数学原理。
定理1 设由n个正整数组成的整数集S里,存在着一个固定长度l的有序数列A {},则由所有满足这种关系的序列所组成的序列集{
},就是S关于l的所有组合。
证明:证明该定理,核心是证明是否所有可能的组合情况都能通过序列A的形式表现出来。
(反证法)假设存在着一种整数组合B不满足A的条件,现设,互换这两个数的位置,仍可以使第k位和第k+1位满足约束关系。但互换这两个位置后,原来的次序大小关系可能会被打破。但因为组成B的数都是正整数,根据自然数的良序关系,通过在B内进行位置的互换,总能找到一个子序列B'使
。因此通过对B内的数字进行位置互换,总有一个办法使B满足A的约束条件。这与假设矛盾,命题成立。
简而言之,就是在整数集内任意取l个值,总能取出一个按序递增(减)的数列。这听起来像废话,但结论却很重要。这给出了一个在工程上遍历所有组合情况的思路。
2、算法步骤
- 以由小(大)到大(小)的顺序按最小步长依次初始化数组的各个元素。其中数组的第一个元素为数集的最小(大)元素。
- 从数组最后一位开始加(减)最小步长
- 当最后一位遇到溢出(溢出的阀值由整数集的大小确定)时,向前(向后)进位。
- 若前(后)一位也继续溢出时,继续向前(后)进位,直至不再溢出,或已经溢出到最前一位,算法结束。
- 输出数组的值,然后继续执行2直至算法结束。
下面证明该算法能遍历所有可能的整数组合。
证明:如果将数组看成是一个n进制数,那么数组的每一位元素就相当于各个数位上的值。因为初始化的时候是以从小到大的顺序排列,且最高位为数集中最小的数字S0。所以初始化时构造出的数值(假设为Xmin)必定是最小的。现在按最小步长增加数组的数值,在数组长度有限和可取数字有限的情况下,必定能取遍(Xmin,Xmax)所有的数字组合。
现假设某个数字组合B不能透过上述算法取得,那么这个数字组合对应的n进制数Vb要么大于Xmax,要么小于Xmin。
先考虑Vb < Xmin的情况,Vb < Xmin,意味着在Vb某个高位上存在着一个整数值v 小于Xmin对应位置的值。我们对数值上的所有位从高到低来进行讨论(因为如果低位数值大,但高位数值小,实际的数值也是比原来小)由于最高位数值已经是最小值,不能再小,所以,只能选择次高位。但次高位除非取最小值S0,否则,无法达到比Xmin小的效果。但一旦取S0,组合B就不再是组合数,与原来的题设矛盾。同样的讨论可以继续进行,对后面各位的讨论会发现,除非取前面相同的数字,否则无法找到另外一个数能使Vb < Xmin。因此Vb 一定不小于Xmin.
再来讨论Vb > Xmax的情况,由于讨论最大值的情况也是相似,可以发现每一位,除非取后面相同的值,否则没法达到Vb > Vmax的效果。
由此,可以明确地知道该算法的确能遍历所有组合数。
3、遍历算法的意义
由于组合数的增长是很可观的,即便计算机硬件技术发展到今天,恐怕也没有哪个程序会去求解所有排列组合情况。遍历算法的意义在于,可以在不需要考虑所有排列组合的情况下,挑出心目中最满意的组合。
还是举上面那个例子,假设每个客人对座位的要求都是挑剔的,如果你是电影院的老板,如果随意给他们编座位,可能马上就收到差评。但是也不可能罗列所有的情况让他们自己去挑。因为50个座位给5个人去挑就已经有差不多几百万种情况。因此可能的方案,是随机挑其中的几种给他们去选。但随机取出来的东西,又不能重复,否则程序就会出现死循环。因此最好的方案,就是把每种情况变成一个数值,通过数值的变化,来实现方案的永不重复。(当然现实生活中,最好的方案,就是由客人自己去挑自己心仪的座位,而不是编好位让他们去选。)
总括而言,遍历算法的意义,在于对一些涉及到排列组合的问题上,能快速寻找到下一个组合情况,并能通过存储组合的数字值来代替存储组合本身,节省计算机资源。