买 X 支付 Y 算法

Buy X Pay for Y Algorithm

我要写"Buy X Pay for Y"算法。 到达我端点的请求是文章列表

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

payFor 变量来自数据库,由包裹折扣 ID 定义

这是我到目前为止写的算法

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 篇文章 1 艺术 1 - 20$ 2 艺术 2 - 30 美元 3 艺术 3 - 40$

如果 minPurchaseQuantity 为 3 且 payFor 为 2,则意味着 Art1 的成本应添加到结果列表中(因为它是最便宜的)

如果 minPurchaseQuantity 为 3 且 payFor 为 1,则表示应将 Art2 和 Art1 的成本添加到结果列表中(现在只有 Art2 添加)

好吧,主要问题是,当 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 获取符合折扣条件的批次(即如果 "take 3, pay for X" 则每批有 3 件商品。带有 [=22= 的文章] 是 "exploded",即如果数量为 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));
}

请参阅 this fiddle 进行演示。

旧答案

计算折扣要容易得多。您可以计算符合折扣条件的捆绑包数量(如果需要 3 件,支付 2 件和 8 件商品,则您有两整包,每包 3 件)。通过计算拿取物品和支付物品之间的差额,乘以捆绑数量和每件物品的价格,可以计算出折扣

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

例子:拍3件,买8件买1件:

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