Partition of an Integer + Number of partitions

删除回忆录丶 提交于 2019-11-29 08:03:09

Essentially what Codor said, plus you don't need to recurse further into part() once you found a partition of the target length since they would be longer:

#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);
}

Output:

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

A sort of "hack" would be to make r an argument of part, pass it along recursively an just print the output if level equals r.

How about this? Have an additional argument passed as reference for r, and increment r each time within the recursion block?

#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;
}

Output comes as:

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

Is this what you are looking for?

The function listed below does what you require - it efficiently enumerates all partitions of an integer myInt, which sizes are PartitionSize and whose parts are always >=MinVal and <=MaxVal.

This function uses a std::vector to store each partition, but a fixed size array can be substituted in lieu of that vector in order to facilitate straightforward porting to plain C.

This is not a recursive function! That's why its code is much longer and complex, but as a bonus it is faster for long partitions and is using less RAM for the stack and the parts/elements of each partition are listed in an ascending order (left-to-right) and the partitions themselves are ordered lexicographically (top-to-bottom).

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);
}

Below is a sample output of this function:

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

If you want to compile it, the helper functions are below:

#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.
I was motivated to create this non-recursive function for a microcontroller that had very few bytes of free RAM left for the stack (it had a lot of program memory, though).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!