使用分区中值的约束打印数字的分区
Printing Partitions of a Number Using a Constraint on Values in Partition
我现在正在上一门计算机科学课程,我最近的作业中的一个问题是:
"整数n的分区是将n写成正整数之和的一种方式。对于例如,对于n=7,分区是1+1+5。编写一个程序,查找所有使用r整数的整数n的分区。例如,n=7的所有分区使用r=3的整数为1+1+5、1+2+4、1+3+3、2+2+3。"
我已经为这个问题挣扎了几天了,我不知道该怎么做。我知道我需要一个递归函数来获取变量,可能还需要一个数组或向量来存储它们,但我不知道从哪里开始。
我并不是要求任何人直接给我这个代码,因为我想自己克服它,但如果一些更有经验的程序员能为我指明正确的方法,那将是非常受欢迎的。
提前感谢!
Tristan
下面列出的函数不是递归的,但它有效地枚举了整数myInt
的所有分区,其大小为PartitionSize
,其部分始终为>=MinVal
和<=MaxVal
。每个分区的部分/元素按升序(从左到右)列出,分区本身按字典顺序(从上到下)排列。
此函数使用std::向量来存储每个分区,但为了方便直接移植到plain C
,可以用固定大小的数组来代替该向量。
我预计,具有相同功能的递归函数会有一个更简单/更短的代码,代价是速度更慢,堆栈使用更多的RAM。
void GenPartitions(const unsigned int myInt,
const unsigned int PartitionSize,
unsigned int MinVal,
unsigned int MaxVal)
{
if ((MaxVal = MaxPartitionVal(myInt, PartitionSize, MinVal, MaxVal)) == 0)
return;
if ((MinVal = MinPartitionVal(myInt, PartitionSize, MinVal, MaxVal)) == unsigned int(-1))
return;
std::vector<unsigned int> partition(PartitionSize);
unsigned int idx_Last = PartitionSize - 1;
unsigned int idx_Dec = idx_Last; //The point that needs to be decremented
unsigned int idx_Spill = 0; //Index where the remainder starts spilling leftwise
unsigned int idx_SpillPrev; //Copy of the old idx_Spill for optimization of the last "while loop".
unsigned int LeftRemain = myInt - MaxVal - (idx_Dec - 1)*MinVal; //The remaining value that needs to be spilled leftwise
partition[idx_Dec] = MaxVal + 1; //Initialize first partition. It will be decremented as soon as it enters the "do" loop.
//std::cout << std::setw(idx_Dec * 3 + 1) << "" << "v" << std::endl; //Show the first Decrement Point
do {
unsigned int val_Dec = partition[idx_Dec] - 1; //Value AFTER decrementing
partition[idx_Dec] = val_Dec; //Decrement at the Decrement Point
idx_SpillPrev = idx_Spill; //For optimization so the last "while loop" does not do unnecessary work.
idx_Spill = idx_Dec - 1; //Index where the remainder starts getting spilled. Before the Decrement Pint (not inclusive)
while (LeftRemain > val_Dec) //Spill the remainder leftwise while limiting its magnitude, in order to satisfy the left-to-right ascending ordering.
{
partition[idx_Spill--] = val_Dec;
LeftRemain -= val_Dec - MinVal; // Adjust remainder by the amount used up (minVal is assumed to be there already)
//std::cout << std::setw(((idx_Spill + 1) * 3) + 1) << "" << "-" << std::endl; //Show the remainder spillage
} //For platforms without hardware multiplication, it is possible to calculate the expression (idx_Dec - idx_Spill)*val_Dec inside this loop by multiple additions of val_Dec.
partition[idx_Spill] = LeftRemain; //Spill last remainder of remainder
//std::cout << std::setw((idx_Spill * 3) + 1) << "" << "*" << std::endl; //Show the last remainder of remainder
char a = (idx_Spill) ? ~((-3 >> (LeftRemain - MinVal)) << 2) : 11; //when (LeftRemain == MinVal) then it computes to 11
char b = (-3 >> (val_Dec - LeftRemain));
switch (a & b) //Switch depending on relative magnitudes of elements before and after the partition[idx]. Cases 0, 4, 8 can never occur.
{
case 1:
case 2:
case 3: idx_Dec = idx_Spill;
LeftRemain = 1 + (idx_Spill - idx_Dec + 1)*MinVal;
break;
case 5: for (++idx_Dec, LeftRemain = (idx_Dec - idx_Spill)*val_Dec; (idx_Dec <= idx_Last) && (partition[idx_Dec] <= MinVal); idx_Dec++) //Find the next value, that can be decremented while satisfying the left-to-right ascending ordering.
LeftRemain += partition[idx_Dec];
LeftRemain += 1 + (idx_Spill - idx_Dec + 1)*MinVal;
break;
case 6:
case 7:
case 11:idx_Dec = idx_Spill + 1;
LeftRemain += 1 + (idx_Spill - idx_Dec + 1)*MinVal;
break;
case 9: for (++idx_Dec, LeftRemain = idx_Dec * val_Dec; (idx_Dec <= idx_Last) && (partition[idx_Dec] <= (val_Dec + 1)); idx_Dec++) //Find the next value, that can be decremented while satisfying the left-to-right ascending ordering.
LeftRemain += partition[idx_Dec];
LeftRemain += 1 - (idx_Dec - 1)*MinVal;
break;
case 10:for (LeftRemain += idx_Spill * MinVal + (idx_Dec - idx_Spill)*val_Dec + 1, ++idx_Dec; (idx_Dec <= idx_Last) && (partition[idx_Dec] <= (val_Dec - 1)); idx_Dec++) //Find the next value, that can be decremented while satisfying the left-to-right ascending ordering. Here [idx_Dec] == [cur]+1.
LeftRemain += partition[idx_Dec];
LeftRemain -= (idx_Dec - 1)*MinVal;
break;
}
while (idx_Spill > idx_SpillPrev) //Set the elements where the spillage of the remainder did not reach. For optimization, going down only to idx_SpillPrev
partition[--idx_Spill] = MinVal; //For platforms without hardware multiplication, it is possible to calculate the expression idx_Spill*MinVal inside this loop by multiple additions of MinVal, followed by another "while loop" iterating from idx_SpillPrev to zero (because the optimization skips these iterations). If, so, then both loops would need to be moved before the "switch statement"
DispPartition(partition); //Display the partition ...or do sth else with it
//std::cout << std::setw((idx_Dec * 3) + 1) << "" << "v" << std::endl; //Show the Decrement Points
} while (idx_Dec <= idx_Last);
}
以下是此函数的输出示例:
SAMPLE OUTPUT OF: GenPartitions(20, 4, 1,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
如果你想编译它,辅助函数如下:
#include <iostream>
#include <iomanip>
#include <vector>
unsigned int MaxPartitionVal(const unsigned int myInt,
const unsigned int PartitionSize,
unsigned int MinVal,
unsigned int MaxVal)
{
if ((myInt < 2)
|| (PartitionSize < 2)
|| (PartitionSize > myInt)
|| (MaxVal < 1)
|| (MinVal > MaxVal)
|| (PartitionSize > myInt)
|| ((PartitionSize*MaxVal) < myInt )
|| ((PartitionSize*MinVal) > myInt)) //Sanity checks
return 0;
unsigned int last = PartitionSize - 1;
if (MaxVal + last*MinVal > myInt)
MaxVal = myInt - last*MinVal; //It is not always possible to start with the Maximum Value. Decrease it to sth possible
return MaxVal;
}
unsigned int MinPartitionVal(const unsigned int myInt,
const unsigned int PartitionSize,
unsigned int MinVal,
unsigned int MaxVal)
{
if ((MaxVal = MaxPartitionVal(myInt, PartitionSize, MinVal, MaxVal)) == 0) //Assume that MaxVal has precedence over MinVal
return unsigned int(-1);
unsigned int last = PartitionSize - 1;
if (MaxVal + last*MinVal > myInt)
MinVal = myInt - MaxVal - last*MinVal; //It is not always possible to start with the Minimum Value. Increase it to sth possible
return MinVal;
}
void DispPartition(const std::vector<unsigned int>& partition)
{
for (unsigned int i = 0; i < partition.size()-1; i++) //DISPLAY THE PARTITON HERE ...or do sth else with it.
std::cout << std::setw(2) << partition[i] << ",";
std::cout << std::setw(2) << partition[partition.size()-1] << std::endl;
}
p.S.
我的动机是为一个微控制器创建这个非递归函数,该微控制器留给堆栈的空闲RAM很少(不过它有很多程序内存)。
相关文章:
- 是否有类似std::lower_bound的函数,而不需要排序/分区输入
- 函数作为模板参数,是否对返回类型强制约束
- 约束和显式模板实例化
- IpOpt拒绝解决不受约束的问题
- 使用C++模板时表达约束
- 如何在 SCIP C++ 接口中获取 MILP 约束矩阵中的系数值
- 按字母顺序对C++问题中的子字符串索引进行分区
- 受约束的成员函数和显式模板实例化
- 具有多种约束(例如重量、体积等)的背包
- 如何查找集合的所有分区 (C++)
- Gecode 与 Z3 用于约束随机化
- librdkafka:rd_kafka_assignment 返回分配分区的偏移量 -1001
- 使用指针在分区函数中实现随机分区点
- 如果原型是本地的,则使用流 I/O C++类型约束将失败
- Coursera DSA 算法工具箱第 4 周第 2 个问题 - 分区纪念品
- 迭代器范围的平衡分区,没有LegacyRandomAccessIterator
- C++打开具有 2 个约束的文件
- 对多个(可能)重叠范围进行分区的最简单算法
- 如何拥有受约束的运算符模板?
- 使用分区中值的约束打印数字的分区