web-dev-qa-db-ja.com

整数分割(アルゴリズムと再帰)

合計数の組み合わせの数を見つける(コード内の変数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);

}

感謝!

20
PlusA

n以下の数値を追加して、mを生成するすべての方法を検討してください。あなたが言ったように、私たちはこれをp(n,m)と呼びます。たとえば、p(7,3)= 8は、以下に示すように3未満の数値から7を作成する8つの方法があるためです:(簡単にするために、常に大きいものから小さいものの順に数値を追加すると仮定できます)

  • 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

これで、これらの組み合わせを2つのグループに分割できます。

  1. 最初の要素がm(= 3この例では= 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_を生成する方法の数です。

  • p(n-m, 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)。この例では:

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の間には1対1の対応があり、それらのサイズは同じです。

size(Group2)=p(n, m-1)を証明する:

定義上、p(n,m-1)は、_m-1_(n未満)以下の数値を追加してmを生成する方法の数です。 Group2の定義を読み直すと、これら2つの定義が互いに同じであることがわかります。 => size(Group2) = p(n, m-1)

21
mhsekhavat

まず、この関数について知りたい以上のことについては、 http://mathworld.wolfram.com/PartitionFunctionP.html を参照してください。

この式に関しては、p(n, m)は最大メンバーが最大でnであるmのパーティションの数として定義されていることに注意してください。

したがって、p(n, m)は、最大メンバーが最大でmであるmのパーティションの数です。最大のメンバーが実際にmであるかどうかに従ってそれらを分割しましょう。

最大メンバーがmであるパー​​ティションの数は、n = m + ...を入力する方法の数です。これは、最大メンバーが最大でmであるn-mのパーティションの数であり、定義上p(n-m, m)

最大メンバーが最大でm-1であるnのパーティションの数は、定義上p(n, m-1)です。

したがって、p(n, m) = p(n-m, m) + p(n, m-1)

4
btilly
        / 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のパーティションの数であり、加数> = kのみを許可します。

OPの再帰式のように、(luiges90が言っているように)それらを1つずつ追加します(多数のゼロの非効率性が追加されます)。ただし、幸いなことに、配列内で非常に高速に計算できます。

#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
Lori

合計がnであり、各加数がm以下であるすべての組み合わせの数として、p(n, m)を示します。ここで重要な点は、次の再帰方程式を証明することです。

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

(1)の左側は、p(n、m)とp(n、m --1)の差です。これらは、加数として少なくとも1つのmを含むすべての組み合わせの数であり、残りは合計はn-m(全体がnになるように)、さらに各加数はm以下です。しかし、それは正確には(1)の右側であるp(n-m、m)を意味します。

明らかに、質問の答えはp(n、n)でなければなりません。

2
Hui Zheng