【问题标题】:Integer Partition (algorithm and recursion)整数分区(算法和递归)
【发布时间】:2012-12-27 11:23:48
【问题描述】:

求和数的多少组合(代码中的变量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);

}

赞赏!

【问题讨论】:

  • 所以你想一个一个地计算每个排列?
  • 我认为是组合,这种情况下3+2和2+3没有区别。
  • 我猜它来自这里:en.wikipedia.org/wiki/…
  • 不一样,这里的函数使用不大于m(小于等于)的数字,但是wiki中的case是使用大于等于k的数字。
  • 我想您希望我们通过组合参数来证明p(n,m) 的递归关系是正确的,但是您忘记为该函数提供组合定义!这不是一个公平的要求。

标签: algorithm recursion integer-partition


【解决方案1】:

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

  • 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

因为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) 是通过添加一些小于或等于m 的数字得到n-m 的方式数。

  • 如果将m 附加到p(n-m, m) 的每个组合中,则将产生Group1 的成员。所以p(n-m, m) &lt;= size(Group1)
  • 如果您删除 Group1 的每个成员的第一个 m,它将导致 p(n-m, m) 的组合。所以size(Group1) &lt;= p(n-m, m)

=&gt; size(Group1) = p(n-m, m)。在我们的示例中:

Group1 p(4, 3) :

  • 7=3+3+1 3+1=4
  • 7=3+2+2 2+2=4
  • 7=3+2+1+1 2+1+1=4
  • 7=3+1+1+1+1 1+1+1+1=4

所以p(n-m,m)的任何成员和Group1之间都是一一对应的,它们的大小是相等的。

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

根据定义,p(n,m-1) 是通过添加一些小于或等于m-1(小于m)的数字得到n 的方法数。如果你重新阅读 Group2 的定义,你会发现这两个定义是相同的。 =&gt; size(Group2) = p(n, m-1)

【讨论】:

  • 谁能告诉我值 3+3+1 3+2+2 3+2+1+1 3+1+1+1+1 在 p(4,3) 之后是如何生成的考虑返回值 0&1。
【解决方案2】:
        / 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】:

    首先要了解更多关于此功能的信息,请参阅http://mathworld.wolfram.com/PartitionFunctionP.html

    至于这个公式,记住p(n, m)被定义为n的最大成员最多为m的分区数。

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

    最大成员为m的分区数是n = m + ...的填充方式数,即n-m的最大成员最多为m的分区数m定义为@987654333 @。

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

    因此p(n, m) = p(n-m, m) + p(n, m-1)

    【讨论】:

      【解决方案4】:

      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)。

      【讨论】:

      • 我只是不明白为什么剩下的是p(n-m,m),可以用其他方式解释吗?最好能中文更详细的解释!
      • @PlusA 不知道用中文回答好不好。让我们这样想:所有在 P(n, m) 中但不在 p(n, m-1) 中的组合的特征是什么?首先,这些组合必须有一个固定成员,即m(否则它也将属于p(n, m-1))。其次,其余成员的总和必须是n-m(s.t.总和是n)。此外,其余成员最多可以是m。因此这些组合的数量应该是p(n-m, m)。清楚吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-28
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 2017-10-11
      • 2017-07-23
      • 2012-11-06
      相关资源
      最近更新 更多