Преимущества рекурсивного и нерекурсивного целочисленного разбиения - PullRequest
0 голосов
/ 29 июня 2019

Может ли рекурсивная реализация функции целочисленного разбиения (с функциональностью, эквивалентной нерекурсивной перечисленной ниже) иметь какие-либо преимущества, кроме более простого / короткого кода?

Функция, перечисленная ниже, перечисляет все фиксированныеразмер разделов целого числа myInt, части которого >=MinVal и <=MaxVal.Части каждого раздела перечислены в порядке возрастания (слева направо), а сами разделы упорядочены лексикографически (сверху вниз).

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

PS
Я был мотивирован для создания этой нерекурсивной функции для микроконтроллера, которыйдля стека оставалось очень мало байт свободной оперативной памяти (однако, было много памяти для программ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...