分治法之整数划分

    一年又这么快的过去了,回想一年,又这么浑浑噩噩的混了一年。

    最近身边的工作,大家都在调性能,当然少不了算法。不得已,时隔4年,我又默默的拿起了算法书。

    今天要讨论的事分治法。

    分治法的设计思路是,将一个难以解决的大问题,分割成一些规模较小的相同问题,方便各个击破,分而治之。

    分治法是否可行的判断标准:1,可分解规模小的问题;2,规模小的问题可解;3,规模小的问题可反求原解。

    大家可以想象递归技术,递归实际是对分治法的巧妙利用。

    今天就讨论使用递归的方法求解整数划分的问题。

    问题描述如下:

    将正整数n表示成一系列正整数之和,n = n1 + n2 + ... + nk, 其中n1 >= n2>=... >=nk >= 1, k>=1。

    正整数n的这种表示称为正整数n的划分。正整数n的不同划分个数称为正整数n的划分数,记做p(n).

    例如正整数6有如下11中不同的划分,p(6) = 11.

    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。

    假设我们将正整数n的所有不同的划分中,将最大加数n1小于等于m的划分个数定义为q(n, m)。q(6, 3)表示正整数6中,最大加数小于等于3的排列个数。这里应该是7。

    那么如何来定义q(n,m)呢?

    我们这里讨论的事分治,递归,那么显然这里我们要用到递归。那么如何来分析这个问题呢?

    如果要递归,必须有个终结者,用于最后的返回。然后就是n和nk直接的关系。

    (1)首先我们需找终结者。分析的方法主要以m为特殊值来进行。

    m = 1

     q(n, 1) = 1, n >= 1;

    最大加数小于等于1的划分只有一种, 1 + 1 + 1 + ... +1

    当m >= n, 由于n的划分中不可能出现,最大加数大于n的情况,所以得出

    q(n, m) = q(n, n),m > = n

    q(n, n) = q(n, n - 1) + 1, 这个等式也很好理解,q(n, n - 1),表示n排列中最大加数小于等于n - 1,那所有排列中只剩下一个了,就是n本身,也就是表示中的1。

    这里需要注意的事q(n, n) = q(n, n -1) + 1的表达式,是在n和m相同情况下成立,这里并没有找到q(n,m)的通用关系,及m < n的时候

    (2)寻找n和nk直接的关系

    当m < n时,q(n, m)有如何呢,这里表示最大加数小于等于m的所有排列。根据上面的p(6)的例子。我们很容易推导出q(n, m) = q(n, m - 1) + x,而x是什么呢。

    比如我们定义n为6,m为4,那么q(6, 4) = q(6, 3) + x, 这里我们知道q(6,4)所有排列中除掉q(6, 3)就剩下,以4开头的排列了。4开头的排列有多少呢?应该是所有2的排列,既所有6 - 4的排列。

    这样我们就推导出通用的关系式了,q(n, m) = q(n, m - 1) + q(n - m, m),这个红色的m如何得到的?当然q(n,m)表示小于等于m的组合,那么q(n - m)里面当然不能大于m了。

    总结下:

    q(n, m) = 1,                     n = 1, m =1

                = q(n, n)              n < m

                = 1 + q(n, n -1)    n = m

                = q (n, m - 1) + q(n - m, m)  n >m >1

    最后我们写出递归的伪代码

int q(int n, int m)
{
    if ((n < 1) || (m < 1)) return 0;
    if ((n == 1) || (m == 1)) return 1;
    if (n < m) return q(n, n);
    if (n == m) return q(n, m - 1) + 1;
    return q(n, m - 1) + q(n - m, m)
}



   


你可能感兴趣的:(分治法之整数划分)