【问题标题】:Partition of an Integer + Number of partitions整数的分区 + 分区数
【发布时间】:2023-04-09 20:41:01
【问题描述】:

整数 n 的分区是将 n 写为正整数之和的一种方式。对于

例如,对于 n=7,分区是 1+1+5。我需要一个能找到所有的程序

整数 'n' 的分区使用 'r' 整数。比如n=7的所有分区

使用r=3 整数是1+1+51+2+41+3+32+2+3

这是我目前所拥有的:

#include <iostream>
#include <vector>

using namespace std;

void print (vector<int>& v, int level){
    for(int i=0;i<=level;i++)
        cout << v[i] << " ";
    cout << endl;
}

void part(int n, vector<int>& v, int level){
    int first; /* first is before last */

    if(n<1) return ;
    v[level]=n;
    print(v, level);

    first=(level==0) ? 1 : v[level-1];

    for(int i=first;i<=n/2;i++){
        v[level]=i; /* replace last */
        part(n-i, v, level+1);
    }
}

int main(){
    int num;
    cout << "Enter a number:";
    cin >> num;

    vector<int> v(num);

    part(num, v, 0);
}

这个程序的输出是:

Enter a number:5
5
1 4
1 1 3
1 1 1 2
1 1 1 1 1
1 2 2
2 3

Process returned 0 (0x0)   execution time : 1.837 s
Press any key to continue.

如何更改我的代码,以便拥有那个 'r' 变量?

编辑:

如果不清楚,'r' 值表示每个分区的整数个数。所以在上面的例子中,如果 r=2,那么分区中只能有两个整数。分区将是 4+1 和 3+2。 'r' 值应由用户输入。

【问题讨论】:

  • “r”是什么意思?
  • @aisbaa: r 是术语数。
  • 所以如果 n = 5 和 r = 2 那么分区 = `5, 1 4, 2 3 - 正确吗?

标签: c++ integer-partition


【解决方案1】:

基本上就是 Codor 所说的,而且一旦找到目标长度的分区,就不需要进一步递归到 part(),因为它们会更长:

#include <iostream>
#include <vector>

using namespace std;

void print (vector<int>& v, int level){
    for(int i=0;i<=level;i++)
        cout << v[i] << " ";
    cout << endl;
}

void part(int n, vector<int>& v, int level, int r){
    int first; /* first is before last */

    if(n<1) return ;
    v[level]=n;
    if( level+1 == r ) {
        print(v, level);
        return;
    }

    first=(level==0) ? 1 : v[level-1];

    for(int i=first;i<=n/2;i++){
        v[level]=i; /* replace last */
        part(n-i, v, level+1, r);
    }
}

int main(){
    int num,r;
    cout << "Enter a number:";
    cin >> num;
    cout << "Enter size (r):";
    cin >> r;

    vector<int> v(num);

    part(num, v, 0, r);
}

输出:

Enter a number:5
Enter size (r):2
1 4
2 3

【讨论】:

    【解决方案2】:

    下面列出的函数可以满足您的要求 - 它有效地枚举整数 myInt 的所有分区,其大小为 PartitionSize,其部分始终为 &gt;=MinVal&lt;=MaxVal

    此函数使用 std::vector 来存储每个分区,但可以用一个固定大小的数组代替该向量,以便于直接移植到 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;
    }
    

    附言
    我的动机是为一个微控制器创建这个非递归函数,该微控制器只有很少字节的空闲 RAM 留给堆栈(但它有很多程序内存)。

    【讨论】:

      【解决方案3】:

      一种“黑客”方法是让r 成为part 的参数,递归地传递它,如果level 等于r,则只打印输出。

      【讨论】:

        【解决方案4】:

        这个怎么样?是否有一个附加参数作为 r 的引用传递,并且每次在递归块内递增 r?

        #include <iostream>
        #include <vector>
        
        using namespace std;
        
        void print (vector<int>& v, int level){
            for(int i=0;i<=level;i++)
                cout << v[i] << " ";
            cout << endl;
        }
        
        void part(int n, vector<int>& v, int level, int &r){
            int first; /* first is before last */
        
            if(n<1) return ;
            v[level]=n;
            print(v, level);
        
            first=(level==0) ? 1 : v[level-1];
        
            for(int i=first;i<=n/2;i++){
                v[level]=i; /* replace last */
                r++;
                part(n-i, v, level+1, r);
            }
        }
        
        int main(){
            int num;
            cout << "Enter a number:";
            cin >> num;
        
            int r = 0;
            vector<int> v(num);
        
            part(num, v, 0, r);
            cout << "r = " << r << endl;
        }
        

        输出如下:

        Enter a number:5 
        1 4 
        1 1 3 
        1 1 1 2 
        1 1 1 1 1 
        1 2 2 
        2 3 
        r = 6
        

        这是你要找的吗?

        【讨论】:

        • 不安静。用户需要输入“r”值。例如,“n”值为 14(由用户输入,如代码所示)。然后用户输入一个“r”值。 'r' 值表示每个分区中的整数个数。如果 'r' 值为 3,则每个分区只能有三个整数。所以 6+6+2。 10+2+2、12+1+1、5+5+4等
        猜你喜欢
        • 1970-01-01
        • 2013-09-01
        • 2020-06-05
        • 1970-01-01
        • 1970-01-01
        • 2012-05-10
        • 1970-01-01
        • 2012-05-01
        相关资源
        最近更新 更多