购买X Pay for Y算法

时间:2019-03-01 12:31:13

标签: c# algorithm price discount

我必须编写“以X支付Y付款”算法。 到达我端点的请求是文章列表

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

payFor变量来自db,由包折扣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付2钱就可以了,但是如果买3付1钱就不起作用了。 有人可以帮我吗?

这是算法的工作方式: 我们有3篇文章 1艺术作品1-20 $ 2 Art2-30 $ 3 Art3-40 $

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

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

1 个答案:

答案 0 :(得分:1)

好吧,主要的问题是,一旦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,付X钱”,则每个都有3件商品。。带有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));
}

请参见this fiddle进行演示。

旧答案

计算折扣要容易得多。您可以计算可享受折扣的捆绑商品数量(如果折扣为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;
}