我发现对于较大的长度和整数,使用递归函数不是一个好的选择,因为它会占用过多的 RAM。使用生成器/可恢复函数(以“yield”值)也太慢了,并且需要一个大型库来使其跨平台。所以这里提供了一个非递归解决方案,用C++编写,可以按照排序顺序生成分区(这非常适合排列)。我发现,对于长度大于等于4的分区,这种方法比看似聪明而简洁的递归解决方案快10倍以上。但对于长度为1-3的情况,性能并不一定更好(不过我不关心短长度,因为无论哪种方法都很快)。
unsigned short myInt = 10;
unsigned short len = 3;
vector<unsigned short> partition(len);
unsigned short last = len - 1;
unsigned short penult = last - 1;
short cur = penult;
unsigned short sum = 0;
fill(partition.begin(), partition.end(), 0);
do {
partition[last] = max(0, myInt - sum);
if (partition[cur + 1] <= partition[cur] + 1) {
do {
cur--;
} while (
cur > 0 &&
accumulate(partition.cbegin(), partition.cbegin() + cur, 0) + (len - cur) * (partition[cur] + 1) > myInt
);
if (cur < 0) {
break;
}
sum++;
partition[cur]++;
for (unsigned short i = cur + 1; i < last; ++i) {
sum = sum - partition[i] + partition[i - 1];
partition[i] = partition[i - 1];
}
cur = penult;
}
else {
sum++;
partition[penult]++;
}
} while (myInt - sum >= partition[penult]);
在这里我写了在这里对“partition”进行操作。是你实际上会使用这个值的地方。(在最后一个迭代中,代码将继续执行循环的其余部分,但我发现这比不断检查退出条件更好,它针对更大的操作进行了优化)
0,0,10
0,1,9
0,2,8
0,3,7
0,4,6
0,5,5
1,1,8
1,2,7
1,3,6
1,4,5
2,2,6
2,3,5
2,4,4
3,3,4
噢,我使用了“unsigned short”,因为我知道我的长度和整数不会超过某些限制,如果不适合您,请更改 :) 请检查注释;那里有一个变量(cur)必须是有符号的,以处理长度为1或2的情况,并且与此相对应的if语句也有,我还在注释中指出,如果您的分区向量具有有符号整数,则可以简化另一行。
要获取所有组合,在C ++中,我将使用这个简单的排列策略,它幸运地不会产生任何重复:
do {
} while (next_permutation(partition.begin(), partition.end()));
将其嵌套在此处的"partition"位置,你就可以开始了。
寻找组合的另一种方法(基于此处的Java代码 https://www.nayuki.io/page/next-lexicographical-permutation-algorithm)如下所示。我发现它比next_permutation()更有效。
composition = partition;
do {
i = last;
while (i > 0 && composition[i - 1] >= composition[i]) {
--i;
}
if (i <= 0) {
break;
}
j = last;
while (composition[j] <= composition[i - 1])
--j;
temp = composition[i - 1];
composition[i - 1] = composition[j];
composition[j] = temp;
j = last;
while (i < j) {
temp = composition[i];
composition[i] = composition[j];
composition[j] = temp;
++i;
--j;
}
} while (true);
你会注意到一些未声明的变量,请在所有do循环之前在代码中声明它们: i、j、pos和temp(无符号短整型),以及composition(与partition相同类型和长度)。在分区代码片段中,可以重复使用i的声明。还要注意像last这样的变量被使用,这假设代码被嵌套在之前给出的分区代码中。
再次提醒,在“Your code goes here”处,您可以根据自己的需要使用这个组合。
以下是我的头文件供参考。
#include <vector>
#include <numeric>
#include <algorithm>
using namespace std;
尽管使用这些方法可以大大提高速度,但对于任何较大的整数和分区长度,这仍然会让您的CPU发疯 :)