整数划分问题
2012-07-29 10:28 coodoing 阅读(503) 评论(0) 编辑 收藏 举报所谓整数划分,就是将一个正整数n划分为一系列的正整数之和,如将n可以划分为:{m1,m2,m3,m4,m5..mk}(1=<k<=n),则{m1,m2,m3,m4,m5..mk}就是n的一个整数划分。下面我们要做的是,要找到所有的n的正整数划分。
我们该如何找出所有的划分呢?
我们可以先来看看整数划分的规律:
譬如正整数:6
划分情况如下:
6
5+1
4+2 4+1+1
3+3 3+2+1 3+1+1+1
2+2+2 2+2+1+1 2+1+1+1+1
1+1+1+1+1+1
观察第一列的数字发现,我们划分过程中总是按照比6小1,小2,小3,小k,的顺序来依次排列它的划分,第一列找到比6小k的数之后,然后我们再排列剩下的数字。
所以我们只需要找到那些比6小的数字开头的,这一行的排列的数目,然后把这些行加起来就是所有的结果,注意看每一行,划分的特征是,所有的数字都不超过第一列的那个数字,所以我们可以进行如下的尝试。
我们试图这样去思考这个问题:
如果{m1,m2,...,mi}中的最大值不超过m,即max(m1,m2,...,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);
例如当n=4时,他有5个划分,{4},{3,1},{2,2},{2,1,1},{1,1,1,1};
注意4=1+3 和 4=3+1被认为是同一个划分。
so,该问题变得清晰了,求出n的所有划分个数,即f(n, n)。
下面我们考虑求f(n,m)的方法;
递归法
先讨论m,n之间的大小关系:
a.当n==1时,m无论为何值,f(n,m)=1
b.当m==1时,n无论为何值,f(n,m)=1
c.当n<m时,由于排列中没有负数,m多出n的数,没有了实际意义,其实本质上 还是等于f(n,n)。
d.当m==n时,可以分两种情况考虑
1.排列中包含n,则f(n,m)=1
2.排列中不包含n,则问题转化为求f(n,m-1)
根据组合数学中加法规则,应将上述两种情况加起来
故此种情况下f(n,m)=1+f(n,m-1);
e.当n>m时,应分两种情况考虑:
1.排列中包含m,则排列为{m,x1,x2,x3,xk},其中{x1,x2,x3,xk}之和为n-m,
我们注意到x1.x2,x3,xk,是不超过m的,所以对于后面这些之和为n-m的元素来说,它们的排列数目应该为f(n-m,m)
2.排列中不包含m,则问题转化为f(n,m-1)
根据组合数学中的加法原则,应将上述情况加起来:
故此情况下的f(n,m)=f(n-m,m)+f(n,m-1);
综合以上情况,我们可以看出,上面的结论具有递归定义特征,其中a和b属于回归条件,c和d属于特殊情况,将会转换为情况e。而情况e为 通用情况,属于递推的方法,其本质主要是通过减小m以达到回归条件,从而解决问题。其递推表达式如下:
f(n, m)= 1; (n=1 or m=1)
f(n, n); (n<m)
1+ f(n, m-1); (n=m)
f(n-m,m)+f(n,m-1); (n>m)
1: public static int divide(int n, int m){
2: if (n < 1 || m < 1) {
3: return 0;
4: } else if (n == 1 || m == 1) {
5: return 1;
6: } else if (n == m) {
7: return 1 + divide(n, n - 1);
8: } else if (n < m) {
9: return divide(n, n);
10: } else {
11: return divide(n, m - 1) + divide(n - m, m);
12: }
13: }
除了递归法解决整数划分问题外,还可以通过生成函数解决。对生成函数的介绍,可以参考http://www.matrix67.com/blog/archives/4534