如何遍历组合数?(不是求组合数,含代码)

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 {A | a{_{n}} \epsilon S, 0 < n <= l, a{_{n-1}} < a{_{n}}},则由所有满足这种关系的序列所组成的序列集{A' | a_{n} \epsilon A, A \epsilon A'  },就是S关于l的所有组合。

证明:证明该定理,核心是证明是否所有可能的组合情况都能通过序列A的形式表现出来。

(反证法)假设存在着一种整数组合B不满足A的条件,现设b{_{k}} > b{_{k + 1}},互换这两个数的位置,仍可以使第k位和第k+1位满足约束关系。但互换这两个位置后,原来的次序大小关系可能会被打破。但因为组成B的数都是正整数,根据自然数的良序关系,通过在B内进行位置的互换,总能找到一个子序列B'使b{_{k}} < b{_{q}}, k < q < l。因此通过对B内的数字进行位置互换,总有一个办法使B满足A的约束条件。这与假设矛盾,命题成立。

简而言之,就是在整数集内任意取l个值,总能取出一个按序递增(减)的数列。这听起来像废话,但结论却很重要。这给出了一个在工程上遍历所有组合情况的思路。

2、算法步骤

  1. 以由小(大)到大(小)的顺序按最小步长依次初始化数组的各个元素。其中数组的第一个元素为数集的最小(大)元素。
  2. 从数组最后一位开始加(减)最小步长
  3. 当最后一位遇到溢出(溢出的阀值由整数集的大小确定)时,向前(向后)进位。
  4. 若前(后)一位也继续溢出时,继续向前(后)进位,直至不再溢出,或已经溢出到最前一位,算法结束。
  5. 输出数组的值,然后继续执行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个人去挑就已经有差不多几百万种情况。因此可能的方案,是随机挑其中的几种给他们去选。但随机取出来的东西,又不能重复,否则程序就会出现死循环。因此最好的方案,就是把每种情况变成一个数值,通过数值的变化,来实现方案的永不重复。(当然现实生活中,最好的方案,就是由客人自己去挑自己心仪的座位,而不是编好位让他们去选。)

总括而言,遍历算法的意义,在于对一些涉及到排列组合的问题上,能快速寻找到下一个组合情况,并能通过存储组合的数字值来代替存储组合本身,节省计算机资源。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章