Home > Net >  Rounding up a variable amount to a given number of units with C#
Rounding up a variable amount to a given number of units with C#

Time:06-10

I am building a shopping list feature as part of a cookery school application with C#.

The shopper needs to know how much of a given ingredient to buy to cover the class needs. For practicality, quantities need to be rounded up to purchase units.

For example.

A class requires 40cm of foil per person. There are 15 persons so we need 600cm of foil for the class. There are 34 classes running so over all we need 600 * 34 = 20400cm of foil.

Foil is bought in rolls of 2000cm. We know that we will have to give each class one roll to cover their needs. We know this because we know each class needs less than a whole roll.

The problem is that I want to be able to mathematically calculate the number of units required reliably from the total of 20400. If I divide the total by the unit:

20400 / 2000 = 10.2 (or rounded up to eleven rolls).

But this is wrong because there are 34 classes and I need to give each class a whole roll. So the correct purchase amount is 34 rolls.

Does anyone know of a way to achieve this from the total require using math? Or to work out how many rolls for each class based on the class requirement?

So

600cm requires 1 roll 2100cm requires 2 rolls 3100 requires 2 rolls 4100 requires 3 rolls

Perhaps a loop that checks the quantity against multiples of the unit quantity? Can anyone suggest a method?

Update: What I really needed was just this from below:

  int rollCount = (int)Math.Ceiling(8060f / 2000f);

CodePudding user response:

Since the requirement is that each class receives an integer number of foil rolls, then the rounding must happen at the class level, and not at the overall school level.

const float foilRollLength = 2000;

public int OrderFoilRolls(int numClasses, int numPersonsPerClass, float minRollPerPerson)
{
    float minRollPerClass = numPersonsPerClass * minRollPerPerson;
    int rollCountPerClass = (int)Math.Ceiling(minRollPerClass/foilRollLength);
    return numClasses * rollCountPerClass;

}

Let's look at your example

public int OrderFoilRolls(34, 15, 40f)
{
    600f = 15 * 40f;
    1 = (int)Math.Ceiling( 600f / 2000f );
    return 34 * 1;

}

Here are some examples of accounting that I did for testing

Classroom    #Students    Foil [ft]    #Rolls
Art 1        23           920          1
Art 2        30           1200         1
Art 3        21           840          1
Art 4        15           600          1
Art 5        7            280          1
Art 6        29           1160         1
Art 7        38           1520         1
Art 8        65           2600         2
Art 9        25           1000         1
Art 10       55           2200         2
Art 11       36           1440         1
Art 12       56           2240         2
Art 13       63           2520         2
Art 14       31           1240         1
Art 15       59           2360         2
Art 16       37           1480         1
Art 17       54           2160         2
Art 18       60           2400         2
Art 19       18           720          1
Art 20       50           2000         1
Art 21       22           880          1
Art 22       10           400          1
Art 23       31           1240         1
Art 24       41           1640         1
Art 25       10           400          1
Art 26       29           1160         1
Art 27       17           680          1
Art 28       29           1160         1
Art 29       42           1680         1
Art 30       64           2560         2
Art 31       53           2120         2
Art 32       36           1440         1
Art 33       10           400          1
Art 34       47           1880         1

In total school needs 43 rolls of foil.

Since this is C# you might as well start using class structures for organizing this information.

My proposal is to have a school class containing the classes, and each classroom is linked with requirements depending on what kind of class it is. Now each class can generate a readonly structure of class supplies, and they can be aggregated together, to sum up all the supplies needed for the school.

This class hierarchy is flexible enough to accommodate multiple supplies, each with its own requirement (per student) and limitation (order minimums). So each classroom may have different requirements and different numbers of students.

Here is the code I used for the example above:

cd1

namespace SO.Q72559914
{
    class Program
    {
        static readonly Random rng = new Random();

        static void Main(string[] args)
        {
            School school = new School();
            ClassRequirements artRequirements = new ClassRequirements()
            {
                FoilPerStudent = 40f,
            };
            // Fill school with classes
            school.AddClass("Art", artRequirements, 34);

            // Fill classes with students
            for (int i = 0; i < school.Classes.Count; i  )
            {
                // pick a random number of students
                school.Classes[i].StudentCount = rng.Next(5, 66);
            }

            Console.WriteLine($"{"Classroom",-12} {"#Students",-12} {"Foil Req'd",-12} {"#Rolls",-6}");
            foreach (var room in school.Classes)
            {
                ClassSupplies classSupplies = room.OrderSupplies();
                Console.WriteLine($"{room.Name,-12} {room.StudentCount,-12} {room.StudentCount * room.Requirements.FoilPerStudent,-12} {classSupplies.FoilRolls,-6}");
            }
            Console.WriteLine();
            ClassSupplies totalSupplies = school.OrderSupplies();

            Console.WriteLine($"In total school needs {totalSupplies.FoilRolls} rolls of foil.");
        }
    }

    public static class SupplyLimits
    {
        public const float FoilLength = 2000f;
    }

    public class ClassRequirements
    {
        public float FoilPerStudent { get; set; }
    }

    public struct ClassSupplies
    {
        public ClassSupplies(int foilRools) : this()
        {
            FoilRolls = foilRools;
        }

        public int FoilRolls { get;  }
        public static ClassSupplies operator  (ClassSupplies a, ClassSupplies b)
            => new ClassSupplies(a.FoilRolls   b.FoilRolls);
    }

    public class Classroom
    {
        public ClassRequirements Requirements { get; set; }
        public int StudentCount { get; set; }
        public string Name { get; set; } = string.Empty;

        public ClassSupplies OrderSupplies()
        {
            float foilRequired = StudentCount * Requirements.FoilPerStudent;
            int foilRolls = (int)Math.Ceiling(foilRequired / SupplyLimits.FoilLength);
            return new ClassSupplies(foilRolls);
        }
    }

    public class School
    {
        public List<Classroom> Classes { get; } = new List<Classroom>();

        public void AddClass(string name, ClassRequirements requirements, int count = 1)
        {
            for (int i = 0; i < count; i  )
            {
                Classes.Add(new Classroom() { Name = $"{name} {i 1}", Requirements = requirements });
            }
        }

        public ClassSupplies OrderSupplies()
        {
            return Classes.Select((room) => room.OrderSupplies()).Aggregate((sum, item) => sum   item);
        }
    }
}

CodePudding user response:

This is not so much a C# / programming question, but an algorithm question.

You assume that

  1. A class requires 40cm of foil per person.
  2. There are 15 persons so we need 600cm of foil for the class.
  3. There are 34 classes running so over all we need 600 * 34 = 20400cm of foil.

and that you can then calculate the number of rolls to be purchased from the 20,400 cm. Instead, you first need to calculate the purchase size per class:

  1. A class requires 40cm of foil per person.
  2. There are 15 persons so we need 600cm of foil for the class.
  3. So for each class you need 600 / 2000 = 0.3 rolls for the class. Round up to one roll per class.
  4. There are 34 classes running so over all we need 34 * 1 = 34 rolls.

CodePudding user response:

Well this seems to work but it's not elegant:

    decimal amnt = 4010;
    decimal packSize = 2000;
    
    int i=0;
    bool y=true;
    while(y){
        if( amnt <= packSize){
            i  ;
            break;
        }
        else if(amnt > packSize) 
        {
            packSize  = packSize; 
        }

        i  ;
    }
    
    Console.WriteLine("Units: "   i);

This returns 3. Which is correct. Because we need three rolls for the class when 4010cm are required.

  • Related