【问题标题】:Count integer partions with k parts, each below some threshold m计算具有 k 个部分的整数部分,每个部分低于某个阈值 m
【发布时间】:2020-08-23 12:57:53
【问题描述】:

我想计算我们可以将数字 n 划分为 k 不同部分的方法的数量,其中每个部分不大于 m

对于k := 2,我有以下算法:

public int calcIntegerPartition(int n, int k, int m) {

  int cnt=0;
  for(int i=1; i <= m;i++){
    for(int j=i+1; j <= m; j++){
      if(i+j == n){
        cnt++;
        break;
      }
    }
  }
  return cnt;
}

但是我如何使用k &gt; 2 计算整数分区?通常我有n &gt; 100000k := 40m &lt; 10000

提前谢谢你。

【问题讨论】:

    标签: algorithm math numbers combinations integer-partition


    【解决方案1】:

    让我们从选择 k 个最大的合法数开始:m, m-1, m-2, ..., m-(k-1)。这加起来为 k*m - k(k-1)/2。如果 m = k。

    假设 p = (km - k(k-1)/2) - n。

    如果 p = 0。请注意,如果 p = 0,则只有一个解,因此我们假设 p > 0。

    现在,假设我们首先选择 k 个最大的不同合法整数,然后我们纠正它以获得解决方案。我们的更正涉及将值向左(在数轴上)移动 1 个插槽,进入空插槽,恰好 p 次。我们有多少种方法可以做到这一点?

    开始的最小值是m-(k-1),它可以向下移动1,最多可以移动m-k次。在此之后,每个后续值都可以向上移动到其前一个移动。

    现在的问题是,有多少个最大值为 m-k 的非增整数序列和 p?这就是分区问题。即,我们可以将 p 分区多少种方式(最多分成 k 个分区)。这不是封闭形式的解决方案。

    有人已经在这里写了这个问题的一个很好的答案(需要稍微修改以满足您的限制):

    Is there an efficient algorithm for integer partitioning with restricted number of parts?

    【讨论】:

    • 整数分区的另一种解释,附代码:algorithm-visualizer.org/dynamic-programming/integer-partition
    • 这并不反映每次分解时每个分区的唯一性。我也只是想知道计数。
    • @TThoEinthausend 它确实反映了独特性,并且链接向您展示了无需枚举解决方案即可获得计数的有效方法。
    【解决方案2】:

    正如@Dave 所暗示的,对于简单的受限整数情况已经有了一个非常好的答案(在这里找到(与@Dave 相同的链接):Is there an efficient algorithm for integer partitioning with restricted number of parts?)。

    下面是C++ 中的一个变体,它考虑了每个受限部分的最大值。首先,这是主力:

    #include <vector>
    #include <algorithm>
    #include <iostream>
    
    int width;
    int blockSize;
    static std::vector<double> memoize;
    
    double pStdCap(int n, int m, int myMax) {
        
        if (myMax * m < n || n < m) return 0;
        if (myMax * m == n || n <= m + 1) return 1;
        if (m < 2) return m;
        
        const int block = myMax * blockSize + (n - m) * width + m - 2;
        if (memoize[block]) return memoize[block];
        
        int niter = n / m;
        
        if (m == 2) {
            if (myMax * 2 >= n) {
                myMax = std::min(myMax, n - 1);
                return niter - (n - 1 - myMax);
            } else {
                return 0;
            }
        }
        
        double count = 0;
        
        for (; niter--; n -= m, --myMax) {
            count += (memoize[myMax * blockSize + (n - m) * width + m - 3] = pStdCap(n - 1, m - 1, myMax));
        }
        
        return count;
    }
    

    如您所见,pStdCap 与链接解决方案非常相似。一个明显的区别是顶部的 2 个额外检查:

    if (myMax * m < n || n < m) return 0;
    if (myMax * m == n || n <= m + 1) return 1;
    

    这里是设置递归的函数:

    double CountPartLenCap(int n, int m, int myMax) {
        
        if (myMax * m < n || n < m) return 0;
        if (myMax * m == n || n <= m + 1) return 1;
        if (m < 2) return m;
        
        if (m == 2) {
            if (myMax * 2 >= n) {
                myMax = std::min(myMax, n - 1);
                return n / m - (n - 1 - myMax);
            } else {
                return 0;
            }
        }
        
        width = m;
        blockSize = m * (n - m + 1);
        memoize = std::vector<double>((myMax + 1) * blockSize, 0.0);
        
        return pStdCap(n, m, myMax);
    }
    

    参数说明:

    1. n 是您要分区的整数
    2. m是每个分区的长度
    3. myMax 是可以出现在给定分区中的最大值。 (OP将此称为阈值)

    这里是现场演示https://ideone.com/c3WohV

    这里是pStdCap 的非记忆版本,它更容易理解。这最初是在Is there an efficient way to generate N random integers in a range that have a given sum or average?的答案中找到的

    int pNonMemoStdCap(int n, int m, int myMax) {
        
        if (myMax * m < n) return 0;
        if (myMax * m == n) return 1;
        
        if (m < 2) return m;
        if (n < m) return 0;
        if (n <= m + 1) return 1;
        
        int niter = n / m;
        int count = 0;
        
        for (; niter--; n -= m, --myMax) {
            count += pNonMemoStdCap(n - 1, m - 1, myMax);
        }
        
        return count;
    }
    

    如果您实际上打算计算大至 10000 的数字的分区数,您将需要一个大的 int 库,如 CountPartLenCap(10000, 40, 300) &gt; 3.2e37(基于 OP 的要求)。

    【讨论】:

    • 感谢您的解决方案:您的情况是什么:n,m,myMax,pcount()
    • @TThoEinthausend 我更新了我的答案。我试图与链接的答案保持一致。如果还不清楚,请告诉我。
    • 从哪里得到变量width的赋值?
    • 你看到live demo了吗?
    猜你喜欢
    • 2020-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多