正如@Dave 所暗示的那样,对于简单的受限整数情况已经有了一个非常好的答案(在这里找到(与@Dave 相同的链接):Is there an effective algorithm for integer partitioning with limited 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);
}
参数说明:
n
是您要分区的整数
m
是每个分区的长度
myMax
是可以出现在给定分区中的最大值。(OP将此称为阈值)
这是一个现场演示https://ideone.com/c3WohV
pStdCap
这是一个更容易理解的非记忆版本。这最初是在这个答案中找到的,Is there an effective way to generate N random integers in a range that has 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) > 3.2e37
(基于 OP 的要求)。