Купить X Pay for Y Алгоритм - PullRequest
0 голосов
/ 01 марта 2019

Мне нужно написать алгоритм «Купи X, заплати за Y».Запрос, который приходит к моей конечной точке, представляет собой список Статей

public class Article
{
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

Переменная payFor поступает из базы данных и определяется идентификатором скидки пакета

Вот мой алгоритм, который я написал до сих пор

if (purchasedQuantity >= minPurchaseQuantity)
{
    var c = 0;
    foreach (var article in articlesForPackage.OrderByDescending(a => a.UnitPrice))
    {
        for (var i = 1; i <= article.Quantity; i++)
        {
            c++;

            if (c > payFor)
            {
                c = 0;

                result.Add(new Discount
                {
                    Value = article.UnitPrice
                });
            }
        }
    }
}

К сожалению, в некоторых случаях этот алгоритм не работает.

Когда определена скидка на пакет, купи 3 и заплати за 2, это сработает, но если купи 3, оплата за одного не сработает.Может ли кто-нибудь помочь мне?

Вот как должен работать алгоритм: у нас есть 3 статьи 1 Art1 - 20 $ 2 Art2 - 30 $ 3 Art3 - 40 $

Если minPurchaseQuantity равно 3 и payFor равно 2, это означает, чтостоимость Art1 должна быть добавлена ​​в список результатов (потому что она самая дешевая)

Если minPurchaseQuantity равно 3, а payFor равно 1, это означает, что стоимость Art2 и Art1 должна быть добавлена ​​в список результатов (теперь только Art2добавляет)

1 Ответ

0 голосов
/ 01 марта 2019

Ну, главная проблема в том, что вы сбрасываете c, как только оно становится больше payFor.Это работает до тех пор, пока minPurchaseQuantity-payFor=1, но в других случаях это не так.

Хотя это не так просто, как решение, представленное в моем первом ответе, я думаю, что настоящий алгоритм может быть реализован более кратко.Следующий код сначала группирует товары в группах, имеющих право на скидку.Затем для каждой из партий она пропускает до payFor элементов и рассчитывает скидку от остальных

// first batch the items in batches eligible for discount (e.g. batches of three in take 3, pay for x)
var batchedItems = BatchItemsEligibleForDiscount(items, minPurchaseQuantity);
var discounts = batchedItems.Select(batch => batch.Skip(payFor)) 
                            .SelectMany(batch => batch) // flatten nested IEnumerable to an IEnumerable<Artible>
                            .Select(article => new Discount() { Value = article.UnitPrice });

. BatchItemsEligibleForDiscount получает партии, которые имеют право на скидку (т. Е. Имеют по 3 позиции, если каждая «возьми 3, заплати за X» . Статьи с Quantity>1 «взорвались» , т. е. если количество равно 3, создаются 3 различных объекта.

IEnumerable<IEnumerable<Article>> BatchItemsEligibleForDiscount(items, minPurchaseQuantity)
{
    return items.OrderByDescending(article => article.UnitPrice)
                .Select(article => Enumerable.Range(1, article.Quantity).Select(n => new Article() { Quantity = 1, UnitPrice = article.UnitPrice })) // "explode" articles 
                .SelectMany(item => item) // flatten to an IEnumerable<Article>
                .Select((article, index) => new { article, index })
                .GroupBy(x => x.index / minPurchaseQuantity)
                .Where(group => group.Count() == minPurchaseQuantity) // only take batches elegible for discount
                .Select(group => group.Select(x => x.article));
}

См. эту скрипку для демонстрации.

СТАРЫЙ ОТВЕТ

Расчет скидки намного проще. Вы можете рассчитать числонаборов, подходящих для скидки (если взять 3, заплатить за 2 и 8 предметов, у вас есть две целые пачки по 3 предмета в каждой) .Рассчитав разницу между предметами, которые нужно взять, и предметами, которые нужно оплатить, и умножив их на количествопачками и ценой за единицу вы можете рассчитать скидку

var numberOfDiscountableBundles = article.Quantity / amountOfItemsElegibleForDiscount; 
var discount = numberOfDiscountableBundles * (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;

Пример: Взять 3, заплатите за 1 с помощью 8 предметов:

numberOfDiscountableBundles = 8 / 3 = 2 (integer division!)
discount = 2 * (3 - 1) * p = 2 * 2 * p = 4 * p

Это двапакеты со скидкой по три штуки в каждой (шесть штук). не оплачиваются (только по одному на пачку), поэтому общая цена дисконтируется в четыре раза больше цены за единицу.

Вы можете заключить это в капсулу методом

Discount CalculateDiscountForArticle(Article article, int amountOfItemsElegibleForDiscount, int payFor)
{
    var numberOfDiscountableBundles = article.Quantity / amountOfItemsElegibleForDiscount; 
    var discount = numberOfDiscountableBundles * (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;

    return new Discount
               {
                   Value = discount
               };
}

И ваша оригинальная функция становится такой же простой, как

var discounts = articlesForPackage.OrderByDescending(a => a.UnitPrice)
                                  .Select(a => CalculateDiscountForArticle(a, amountOfItemsElegibleForDiscount, payFor));

РЕДАКТИРОВАТЬ НА СТАРЫЙ ОТВЕТ

Если скидка предоставляется только один раз для клиента и товара,расчет немного отличается

double discount = 0;
if(article.Quantity >= amountOfItemsElegibleForDiscount)
{
    var discount = (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;
}
...