整数分区(算法和递归)

时间:2012-12-27 11:23:48

标签: algorithm recursion integer-partition

查找总和数的多少组合(代码中的变量 n )。例如:

  

3 = 1 + 1 + 1 = 2 + 1 = 3 => ANS是3

     

5 = 5 = 4 + 1 = 3 + 2 = 3 + 1 + 1 = 2 + 2 + 1 = 2 + 1 + 1 + 1 = 1 + 1 + 1 + 1 + 1 => ANS是7

在以下示例中, m 是最大数字,n是总和, 目的是找出它有多少(总和)组合。

我只是想知道为什么p(n, m) = p(n, m - 1) + p(n - m, m)

这里的代码:

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

    return p(n, m - 1) + p(n - m, m);

}

赞赏!

4 个答案:

答案 0 :(得分:21)

通过添加小于或等于n的某些数字,考虑所有生成m的方法。如你所说,我们称之为p(n,m)。例如,p(7,3)= 8,因为有8种方法可以使7个数字小于3,如下所示:(为简单起见,我们可以假设始终按照从最大到最小的顺序添加数字)

  • 3 + 3 + 1
  • 3 + 2 + 2
  • 3 + 2 + 1 + 1
  • 3 + 1 + 1 + 1 + 1
  • 2 + 2 + 2 + 1
  • 2 + 2 + 1 + 1 + 1
  • 2 + 1 + 1 + 1 + 1 + 1
  • 1 + 1 + 1 + 1 + 1 + 1 + 1

现在我们可以将这些组合分成两组:

  1. 第一个元素等于m的组合(在我们的示例中= 3)

    • 3 + 3 + 1
    • 3 + 2 + 2
    • 3 + 2 + 1 + 1
    • 3 + 1 + 1 + 1 + 1
  2. 第一个元素小于m的组合:

    • 2 + 2 + 2 + 1
    • 2 + 2 + 1 + 1 + 1
    • 2 + 1 + 1 + 1 + 1 + 1
    • 1 + 1 + 1 + 1 + 1 + 1 + 1
  3. 因为p(n,m)组合的每个成员都在Group1或Group2中,我们可以说p(n,m)=size(Group1) + size(Group2)。现在,如果我们通过替换证明size(Group1)=p(n-m, m)size(Group2)=p(n,m-1),我们就会达到p(n,m)=p(n-m,m)+p(n,m-1)

    证明size(Group1)=p(n-m, m)

    按照上述定义,p(n-m, m)是通过添加小于或等于n-m的某些数字来生成m的方式的数量。

    • 如果您将m附加到p(n-m, m)的每个组合,则会产生Group1的成员。所以p(n-m, m) <= size(Group1)
    • 如果您删除了Group1的每个成员的第一个m,则会产生p(n-m, m)的组合。所以size(Group1) <= p(n-m, m)

    => size(Group1) = p(n-m, m)。在我们的例子中:

    组1&lt; ===对应===&gt; p(4,3):

    • 7 = 3 + 3+1&lt; ===========&gt; 3+1 = 4
    • 7 = 3 + 2+2&lt; ===========&gt; 2+2 = 4
    • 7 = 3 + 2+1+1&lt; =======&gt; 2+1+1 = 4
    • 7 = 3 + 1+1+1+1&lt; ===&gt; 1+1+1+1 = 4

    因此p(n-m,m)的任何成员与Group1之间存在一对一的对应关系,且其大小相等。

    证明size(Group2)=p(n, m-1)

    根据定义,p(n,m-1)是通过添加小于或等于n(小于m-1)的某些数字来获得m的结果的数量。如果您重新阅读Group2的定义,您将看到这两个定义彼此相同。 => size(Group2) = p(n, m-1)

答案 1 :(得分:4)

首先,您需要了解此功能,请参阅http://mathworld.wolfram.com/PartitionFunctionP.html

对于此公式,请记住p(n, m)被定义为最大成员最多n的{​​{1}}分区数。

因此mp(n, m)的分区数,其最大成员最多为m。让我们根据最大成员实际是m来划分它们。

最大成员为m的分区数是填充m的方式数,n = m + ...是最大成员最多n-m的{​​{1}}分区数根据定义,m

p(n-m, m)的最大成员最多n的分区数量定义为m-1

因此p(n, m-1)

答案 2 :(得分:3)

        / 0 (k>n)
p(k,n)={  1 (k=n)
        \ p(k+1,n)+p(k,n-k) (k<n)

n的分区数是p(1,n)。

p(k,n)是n的分区数, 只允许加数&gt; = k。

与OP的递归公式一样,它逐个添加它们(如luiges90所示)(增加了许多零的低效率)。幸运的是,它可以在数组内以极快的速度计算:

#include <stdio.h>

/* 406 is the largest n for which p(n) fits in 64 bits */
#define MAX 406
long long int pArray[MAX][MAX];

/* Emulate array starting at 1: */
#define p(k,n) pArray[k-1][n-1]

int main(int argc, char **argv) {

 int n, k;

 for (n = 1; n < MAX; n++) {
  for (k = n; k > 0; k--) {
   if (k > n) {
    p(k, n) = 0;
   }
   else if (k == n) {
    p(k, n) = 1;
   }
   else {
    p(k, n) = p(k, n-k)+p(k+1, n);
   }
  }
 }

 for (n = 1; n < MAX; n++) {
  printf("p(%d)=%lld\n", n, p(1,n));
 }

}

答案 3 :(得分:2)

p(n, m)表示为总和为n且每个加数小于或等于m的所有组合的数量。这里的关键是证明以下递归方程:

p(n, m) - p(n, m - 1) = p(n-m, m)          (1)

(1)的左边是p(n,m)和p(n,m - 1)的差值,它是包含至少一个m作为加数的所有组合的数量,剩余的总和为n-m(总体为n),除了每个加数小于或等于m。但这恰恰意味着p(n-m,m),这是(1)的右侧。

显然,问题的答案应该是p(n,n)。