枚举具有以下2个限制的正整数的所有分区时:
PartitionSize
MaxVal
,并且大于零。...我面临着对这些分区进行编号/索引的任务,我可以存储它们的索引,以后再检索它们以从任意索引快速重新生成一个分区的元素。索引不必是连续的。
Q :计算此类分区索引的最佳方法是什么?
下面列出了生成这些分区的函数:
void GenPartitions(const unsigned int myInt, const unsigned int PartitionSize, unsigned int MaxVal)
{
if ((MaxVal = MaxPartitionVal(myInt, PartitionSize, MaxVal)) == 0)
return;
unsigned int MinVal = 1;
unsigned int idx_Last = PartitionSize - 1;
unsigned int RightSum = MaxVal; //Sum to the right of the Decrement Point (inclusive)
unsigned int idx_Dec = idx_Last; //The point that needs to be decremented
vector<unsigned int> partition(PartitionSize);
partition[idx_Last] = MaxVal; //Initiallize first partition
do {
unsigned int cur = idx_Dec - 1;
unsigned int LeftRemain = myInt - RightSum - (idx_Dec - 1) * MinVal; //Calculate the remainder to the left
while (LeftRemain > partition[idx_Dec]) //While the remainder is too big to satisfy the left to right ascending ordering.
{
LeftRemain -= partition[idx_Dec] - 1; //
partition[cur--] = partition[idx_Dec];
}
partition[cur] = LeftRemain; //Last remainder
for (unsigned int i = 0; i < cur; i++) //Set the elements where the reminder did not reach.
partition[i] = MinVal;
for (auto d : partition) //DISPLAY THE PARTITON HERE ...or do sth else with it.
std::cout << setw(2) << d << ",";
std::cout << endl;
for (idx_Dec = 0; (idx_Dec < idx_Last) && (partition[idx_Dec] + 1 > partition[idx_Dec + 1]); idx_Dec++); //Find the rising edge
unsigned int val_1stUp = partition[idx_Dec]+1;
for (++idx_Dec; (idx_Dec <= idx_Last) && (val_1stUp > partition[idx_Dec] - 1); idx_Dec++); //Find the falling edge occuring AFTER the rising edge.
if (idx_Dec > idx_Last)
break; //Could not find the falling edge. We are done.
partition[idx_Dec]--; //Decrement at the Decrement Point
//std::cout << setw((idx_Dec*3)+1) << "" << "v" << endl; //Show the Decrement Points
RightSum = 0; //This needs optimization. There is no need to start from the Decrement Point every time. This sum can be adjusted on-the-go, as changes are made to the partition.
for (unsigned int i = idx_Dec; i <= idx_Last; i++) //Calculate the sum to the right of the Decrement Point (inclusive). This needs optimization.
RightSum += partition[i];
} while(true);
}
请注意,此函数会生成分区,其中每个分区中的所有元素从最小到最大(从左到右)排序。此功能不会损坏。
分区本身之间的顺序(垂直)按字典顺序排列。我不愿意失去它,但没有它我可以活下去。
SAMPLE OUTPUT OF: GenPartitions(20, 4, 10):
1, 1, 8,10
1, 2, 7,10
1, 3, 6,10
2, 2, 6,10
1, 4, 5,10
2, 3, 5,10
2, 4, 4,10
3, 3, 4,10
1, 1, 9, 9
1, 2, 8, 9
1, 3, 7, 9
2, 2, 7, 9
1, 4, 6, 9
2, 3, 6, 9
1, 5, 5, 9
2, 4, 5, 9
3, 3, 5, 9
3, 4, 4, 9
1, 3, 8, 8
2, 2, 8, 8
1, 4, 7, 8
2, 3, 7, 8
1, 5, 6, 8
2, 4, 6, 8
3, 3, 6, 8
2, 5, 5, 8
3, 4, 5, 8
4, 4, 4, 8
1, 5, 7, 7
2, 4, 7, 7
3, 3, 7, 7
1, 6, 6, 7
2, 5, 6, 7
3, 4, 6, 7
3, 5, 5, 7
4, 4, 5, 7
2, 6, 6, 6
3, 5, 6, 6
4, 4, 6, 6
4, 5, 5, 6
5, 5, 5, 5
此外,由于递归解决方案对非常大的分区(尽管实现起来较简单)的性能低以及RAM /堆栈的影响,我特意选择不要将其实现为递归函数。
如果有人要编译下面的帮助函数。
#include <iostream>
#include <iomanip>
#include <vector>
unsigned int MaxPartitionVal(const unsigned int myInt, const unsigned int PartitionSize, unsigned int MaxVal)
{
if ((myInt < 2) || (PartitionSize < 2) || (MaxVal < 1) || (PartitionSize > myInt) || (myInt > (PartitionSize*MaxVal))) //Sanity checks
return 0;
unsigned int last = PartitionSize - 1;
if (MaxVal + last > myInt)
MaxVal = myInt - last; //It is not always possible to start with the MaxValue. Decrease it to sth possible
return MaxVal;
}
答案 0 :(得分:1)
提供此答案的目的是希望它有用,但并不能保证它是最佳的:)。
首先,一些typedef(根据需要进行更改):
using iType = uint_fast64_t; // Type of the generated indices.
using pType = unsigned; // Type of the parts in a partition.
using pSize = std::vector<pType>::size_type; // Size of a partition.
符号:
parts(num, size, max)
是num
的整数分区的集合,其size
部分低于或等于max
。p
是parts
的元素(一个std::vector
,因此索引为0)。getIndex(p, num, size, max)
计算p
的索引。getPartition(index, num, size, max)
计算给定index
的分区。由于索引不必是连续的,因此我们可以这样改述问题:
getIndex(...)
将多个整数多路复用(或压缩)为单个整数。getPartition(...)
将单个整数解复用(或解压缩)为原始整数。一个常见的解决方案是:
由于我们知道分区的每个part
都会验证1 <= part && part <= max
,因此第一个实现可以是:
iType getIndex(const std::vector<pType>& partition, pType max) {
pSize i = partition.size();
iType result = 0;
while (i > 0) {
i--;
const pType iMin = 1;
const pType iMax = max;
pType part = partition[i];
result = result*(iMax+1-iMin) + (part-iMin);
}
return result;
}
std::vector<pType> getPartition(iType index, pSize size, pType max) {
std::vector<pType> result(size,0);
iType currentIndex = index;
for (pSize i = 0; i < size; i++) {
const pType iMin = 1;
const pType iMax = max;
pType divider = iMax + 1 - iMin;
result[i] = iMin + currentIndex % divider;
currentIndex = currentIndex / divider;
}
return result;
}
这有效,但是计算的索引很大。获得较低索引的技巧是使用我们正在处理分区而不是在[1; max]中的运算向量上的事实,在每次循环迭代时计算iMax
和iMin
的更精细的值。
添加自我施加的约束:
p[i] >= p[i+1]
我们可以为p
中的parts(num, size, max)
推论:
p[0] >= 1 + (num-1) / size
p[0] <= num + 1 - size
可以通过将p[i]
放在p[1..size-1]
中来将约束2和3递归应用于所有parts(num-p[0], size-1, p[0])
因此,我们可以计算出更好的iMin
和iMax
,并将它们注入之前的实现中:
// !! Requires a sorted partition, from greatest to lowest part.
iType getIndex2(const std::vector<pType>& partition, pType max) {
pSize size = partition.size();
iType result = 0;
pType currentNum = 0;
pSize i = partition.size();
while (i > 0) {
i--;
pType part = partition[i];
currentNum = currentNum + part;
pType iMax = currentNum+1-(size-i); // constraint 3
if (i > 0) {
iMax = std::min<pType>(iMax, partition[i-1]); // constraint 1
} else {
iMax = std::min<pType>(iMax, max);
}
pType iMin = 1+(currentNum-1)/(size-i); // constraint 2
result = result*(iMax+1-iMin) + (part-iMin);
}
return result;
}
std::vector<pType> getPartition2(iType index, pType num, pSize size, pType max) {
std::vector<pType> result(size,0);
iType currentIndex = index;
pType iMax = std::min<pType>(max, num + 1 - size); // constraint 3
pType currentNum = num;
for (pSize i = 0; i < size; i++) {
pType iMin = 1+(currentNum-1)/(size-i); // constraint 2
pType diviser = iMax+1-iMin;
result[i] = iMin + currentIndex % diviser;
currentIndex = currentIndex / diviser;
currentNum = currentNum - result[i];
iMax = std::min<pType>(result[i], currentNum + 1 - (size - i -1)); // constraint 1 & 3 for step (i+1)
}
return result;
}
iType
何时溢出(并检查它是否对您足够)。我不知道索引根据num
,size
和max
增长的速度。