2

So im writing a small little c# console app, which is supposed to imitate a shopping system of sorts. All the products are sold in Packs (not individually), and the Pack sizes are like 4 items, 10 items or 15 items.

Given a Qty size of like 28, it should return a result of something like: 2 x 10 Packs + 2 x 4 Packs. Or given a Qty size of like 25, it should return a result of something like: 1 x 15 Packs + 1 x 10 Packs And the function needs to be efficient, so it returns the least amount of packs. (obviously ignore scenerio's where it might be an Qty of 3 - as there are no pack sizes that small)

this is my code at the moment:
qty = 28, LargePack = 15, MediumPack = 10, SmallPack = 4

            double total = 0.00;
            int tmpQty = qty;

            while (tmpQty != 0)
            {
                if ((tmpQty >= LargePack) && ((tmpQty % LargePack) % 1) == 0)
                {
                    tmpQty -= LargePack;
                    lg += 1;
                    total =+ (lg * LargePrice);
                }
                else if ((tmpQty >= MediumPack)) 
                {
                    tmpQty -= MediumPack;
                    md += 1;
                    total =+ (md * MediumPrice);
                }
                else if ((SmallPack !=0) && (tmpQty >= SmallPack) && (tmpQty < MediumPack))
                {
                    tmpQty -= SmallPack;
                    sm += 1;
                    total =+ (sm * SmallPrice);
                }
            }

My idea here - because with a qty of like 28, i need it to skip the first IF statement, i thought i'd do a check of whether tmpQty (28) / LargePack (15) was an integer number - which it shouldn't be, then it would go to the 2nd IF statement. But for some reason the formula:

(tmpQty % LargePack) %1 == 0

always equals 0 - so its True... when it should be false.

Or if anyone has a better way to work out Pack sizes, im open to suggestions. Note - Pack sizes aren't defined here, as various different products use the same sorting process.

BlissSol
  • 374
  • 10
  • 23

4 Answers4

1

Your biggest issue is you don't understand what % does. It doesn't return a decimal. It works like the "remainder" from gradeschool division. 7 % 5 will return 2. If you want to keep your same logic (expecting a percentage) then you need to divide but your variables will need to be doubles or floats. If not, C# will cast the result to an integer, which has no decimal. I hope that helps.

John Lord
  • 1,941
  • 12
  • 27
0

(Anything % 1) always returns 0. That's why your condition is not working.

Try this

(tmpQty % LargePack) >= 0 
HarryPotter
  • 119
  • 7
  • (tmpQty % LargePack) >= 0 wont work, as it will always return true for any number above 0. (tmpQty % LargePack) should return a number of 1.86, as this isn't an integer, then i want it to return false. – BlissSol Jun 26 '19 at 16:44
  • 1
    @BlissSol Why do you think `tmpQty % LargePack` (an integer mod operation) should return a floating point number? – NetMage Jun 26 '19 at 19:21
  • @JohnLord yes.. "(Anything % 1) always returns 0" – Blorgbeard Jun 26 '19 at 20:09
0

Use:

if ((tmpQty >= LargePack) && ((tmpQty % LargePack) == 0))

This will return true only for values that are exactly divisible by LargePack, so in this case 28 would fail and move on.

RtmM
  • 1
  • 1
0

Here is a brute force approach. At first we need a method to generate all combinations of an array of sequences. It is called CartesianProduct, and an implementation is shown below:

public static IEnumerable<T[]> CartesianProduct<T>(IEnumerable<T>[] sequences)
{
    IEnumerable<IEnumerable<T>> seed = new[] { new T[0] };
    return sequences.Aggregate(
        seed,
        (accumulator, sequence) => accumulator
            .SelectMany(_ => sequence, (product, i) => product.Append(i))
    ).Select(product => product.ToArray());
}

We will need this method because we need all possible combinations of pack sizes and quantities. For example for pack sizes [5, 8] and quantity 21, the combinations we want to produce are these:

0*5 + 0*8 = 0
0*5 + 1*8 = 8
0*5 + 2*8 = 16
1*5 + 0*8 = 5
1*5 + 1*8 = 13
1*5 + 2*8 = 21
2*5 + 0*8 = 10
2*5 + 1*8 = 18
2*5 + 2*8 = 26
3*5 + 0*8 = 15
3*5 + 1*8 = 23
3*5 + 2*8 = 31
4*5 + 0*8 = 20
4*5 + 1*8 = 28
4*5 + 2*8 = 36

The last step will be to select the best combination. Here is the method that makes all these to happen:

private static (int PackSize, int Quantity)[] GetBestCombination(
    int[] packSizes, int quantity)
{
    var sequences = packSizes
        .Select(p => Enumerable.Range(0, (quantity / p) + 1)
            .Select(q => p * q))
        .ToArray();
    var cartesianProduct = CartesianProduct(sequences);
    int[] selected = null;
    int maxValue = Int32.MinValue;
    foreach (var combination in cartesianProduct)
    {
        int sum = combination.Sum();
        if (sum > quantity) continue;
        if (sum > maxValue)
        {
            maxValue = sum;
            selected = combination;
        }
    }
    if (selected == null) return null;
    return selected.Zip(packSizes, (sum, p) => (p, sum / p)).ToArray();
}

Lets test it:

var bestCombo = GetBestCombination(new[] { 5, 8 }, 21);
foreach (var pair in bestCombo)
{
    Console.WriteLine($"{pair.Quantity} x {pair.PackSize}");
}

Output:

1 x 5
2 x 8

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104