【问题标题】:How to convert a number to a range of prices如何将数字转换为价格范围
【发布时间】:2010-04-21 15:24:15
【问题描述】:

我想计算在客户购买我的产品许可证时向他们收取的费用。

我以不同的许可证出售它:

  • 1-10:50 美元/用户
  • 11-20 : $40/用户
  • 21-30 : $30/用户
  • 31-50 : $20/用户

所以当有人购买 136 个许可证时,我会向他收费:

50 x 2 x $20 = $2000
30 x 1 x $30 = $900
     6 x $50 = $300

我正在寻找一种关于如何处理给定数字并将其分解为一个范围内出现次数的算法.. 如何在纯 C# 或 LINQ 中做到这一点?

------------ 编辑 ----------------

我提出了一个不那么令人困惑的问题 (Algorithm for Fogbugz pricing scheme),我得到了我一直在寻找的答案。

谢谢大家..

【问题讨论】:

  • 您不允许使用 136 个许可证。
  • 70 个许可证的收费是多少? (50 * $20) + (20 * $40) = $1800,或者 (49 * $20) + (21 * $30) = $1610?
  • (50 * $20) + (20 * $40) = $1800.. 好电话马克..
  • 为什么不简化您的定价,根据购买的许可证数量,按照每位用户的单一价格计算?
  • 您可以花 660 美元获得 16 个许可证,或者您可以花 620 美元获得 31 个许可证——节省 40 美元并丢弃 15 个许可证。这是一个非常有趣的方案。

标签: c# linq algorithm range integer


【解决方案1】:

如果采用这种价格结构,我认为通过购买最适合他们需要的套餐来最小化成本符合客户的最大利益。以下算法使用动态规划来计算准确购买一定数量许可证的最低可能价格(您可以通过购买超出需要的数量来省钱,尽管我没有实现):

int getPrice(int n)
{
    if (n >= 1 && n <= 10) return 50 * n;
    if (n >= 11 && n <= 20) return 40 * n;
    if (n >= 21 && n <= 30) return 30 * n;
    if (n >= 31 && n <= 50) return 20 * n;
    throw new Exception("Impossible");
}

int minimizePrice(int n)
{
    int[] minimumPrice = new int[n + 1];
    for (int i = 1; i <= n; ++i)
    {
        minimumPrice[i] = int.MaxValue;
        for (int j = Math.Max(0, i - 50); j < i; ++j)
        {
            minimumPrice[i] = Math.Min(minimumPrice[i],
                minimumPrice[j] + getPrice(i - j));
        }
    }
    return minimumPrice[n];
}

对于 70 个许可证,最低价格为 1400 美元,可通过购买 2 个 35 个许可证的块获得。你在建议一个贪心算法。这会让您的客户感到困惑。聪明的顾客会下两单而不是大单,这样可以节省 400 美元。

我建议您更改价格,以便您可以以每个 20 美元购买的许可证数量没有上限。

【讨论】:

  • 马克,没有上限。。你可以买50多个,但你是分批买50个,就是这样。但我明白你的意思。如果我说 >50 = $20 看起来会更好..
  • 马克,我也明白你关于贪心算法的观点。我从 Fogbugz 复制,但现在我想你是对的。顺便说一句,客户不能合并许可证,因为正在运行单个服务器。
  • 马克,我测试了算法。如果你放 11 个许可证,它会给你 440 美元,这是错误的;它应该是 &540.. 在您创建的数组中,直到第 10 个元素是正确的,第 11 个是错误的。
  • 从商业角度来看,我不明白批量的想法。如果你在卖西红柿,那么批量生产是有意义的,因为运送一定容量的卡车需要支付固定成本。对于软件,没有这样的成本:您的目标是一次销售尽可能多的许可证。不断下降的单价对我来说很有意义。
  • @Anon1865 是的,但支持成本是 IMO 许可证总数的函数,而不是它如何分批中断。重要的是您需要支持 60 个许可证,而不是 50 个许可证 + 10 个许可证。
【解决方案2】:

这看起来与为购买进行找零的算法(选择哪些硬币)非常相似。唯一的区别是您要与一个范围而不是单个数字进行比较。

代码可能如下所示:

var val = 136;
var price = 0;
while (val > 0) 
{
  var range = FindMatchingRange(val); // Use a dictionary, list, or array.
  var number = Math.Min(val, range.Max);
  price += range.CostPerUser * number;
  val -= number;
}

【讨论】:

    【解决方案3】:

    如果我是一个需要 10 个许可证的人,根据您建议的定价计划,我为什么只购买 10 个许可证?

    10 个许可证 * 50 美元/许可证 = 500 美元

    11 个许可证 * 40 美元/许可证 = 440 美元

    您想要的是降低最近购买的许可证成本的计划。因此,对于想要 11 个许可证的人,他们会支付:

    (10 个许可证 * 50 美元/许可证)+(1 个许可证 * 40 美元/许可证)= 540 美元

    一个可能的计划如下所示:

    first 10 licenses (1-10): $50/user
    next 10 licenses (11-20): $40/user
    next 10 licenses (21-30): $30/user
    all licenses after that (31+) : $20/user
    

    编写代码来计算任意数量用户的最终成本是一项简单的练习。购买 136 个许可证的计算如下:

    (10 个许可 * 50 美元/许可) + (10 个许可 * 40 美元/许可) + (10 个许可 * 30 美元/许可) + (106 个许可 * 20 美元/许可) = 500 美元 + 400 美元 + 300 美元 + 2120 美元 = 3,220 美元。

    在我看来,最初的定价计划很古怪。以去年购买了 130 个许可证的客户为例,该客户回来后还想要 10 个许可证。有什么理由向他们收取最高费率?他们是大批量客户,您想以最低的“边际”价格向他们出售(并且他们有理由期望收到)额外的许可证。

    【讨论】:

    • Herbert,该计划取自 fogcreek.com/FogBugz/PriceList.html.. 似乎正在为 Joel 工作......
    • 赫伯特,伟大的 cmets。在您的示例中,我正在考虑向客户收取 50 到 60 个许可证的差额,以换取他额外购买的 10 个许可证。
    • 它可能工作的事实并不意味着它是最佳的。如果我是一个之前购买了 50 个许可证的客户,然后我回来说我还想要 10 个,我希望并希望您考虑到我已经是批量购买者。这种较低的价格取决于你现在购买的数量(或另一种命名为“固定价格每任意数量分组”)的优势是什么?是,对于供应商还是客户?我想我看到了一个小的供应商优势:无需跟踪客户以前的购买记录。作为客户,我不喜欢这样。
    【解决方案4】:

    我为你做了一个计算课……只是更以客户为导向。 它会根据您定义的价格范围计算最便宜的价格。

    示例:136 个许可证

    50 个许可证,每个 20 美元(因为:31-50 :20 美元/用户)

    50 个许可证,每个 20 美元(因为:31-50 :20 美元/用户)

    36 个许可证,每个 20 美元(因为:31-50 :20 美元/用户)

    总计:1720


    示例 130 许可证

    50 个许可证,每个 20 美元

    50 个许可证,每个 20 美元

    30 个许可证,每个 30 美元

    总计:1900


    课程代码:

       public class PriceCalculator
        {
            public List<OrderPackage> CalculateCheapestPrice(Int32 AmountOfLicenses, 
                List<PriceRange> PriceRanges, out Double Total)
            {
                List<OrderPackage> result = new List<OrderPackage>();
                Total = 0;
    
                Int32 AmountsOfLicensesleft = AmountOfLicenses;
    
                PriceRanges.Sort(ComparePrice);
    
                for (int i = 0; i < PriceRanges.Count; i++)
                {
                    for (int j = PriceRanges[i].MaxAmount; j >= PriceRanges[i].MinAmount; j--)
                    {
                        if (j <= AmountsOfLicensesleft)
                        {
                            OrderPackage Order = new OrderPackage();
                            Int32 AmountOfThisPackage = AmountsOfLicensesleft / j;
                            //Int32 AmountForThisPrice = Convert.ToInt32(Math.Floor(tmp));
    
                            Order.PriceRange = PriceRanges[i];
                            Order.AmountOfLicenses = j;
    
                            Total += Order.AmountOfLicenses * Order.PriceRange.PricePerLicense;
    
                            for (int k = 0; k < AmountOfThisPackage; k++)
                            {
                                result.Add(Order);
                            }
    
                            AmountsOfLicensesleft = AmountsOfLicensesleft - (AmountOfThisPackage * j);
                        }
                    }
                }
    
                return result;
            }
    
            private static int ComparePrice(PriceRange x, PriceRange y)
            {
                if (x.PricePerLicense == y.PricePerLicense)
                    return 0;
    
                if (x.PricePerLicense > y.PricePerLicense)
                    return 1;
    
                if (x.PricePerLicense < y.PricePerLicense)
                    return -1;
    
                return 0;
            }
    
            public class OrderPackage
            {
                public PriceRange PriceRange { get; set; }
                public Int32 AmountOfLicenses { get; set; }
            }
    
            public class PriceRange
            {
                public int MinAmount { get; set; }
                public int MaxAmount { get; set; }
    
                public Double PricePerLicense { get; set; }
            }
        }
    

    用法示例:

    private void button1_Click(object sender, EventArgs e)
    {
        // Preparing PriceRangeDefinitions
        List<PriceCalculator.PriceRange> PriceRangeDefinitions = new List<PriceCalculator.PriceRange>();
        PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 1, MaxAmount = 10, PricePerLicense = 50 });
        PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 11, MaxAmount = 20, PricePerLicense = 40 });
        PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 21, MaxAmount = 30, PricePerLicense = 30 });
        PriceRangeDefinitions.Add(new PriceCalculator.PriceRange() { MinAmount = 31, MaxAmount = 50, PricePerLicense = 20 });
    
        // Start the Calculation
        PriceCalculator calculator = new PriceCalculator();
        Double Total;
        List<PriceCalculator.OrderPackage> Packages =
            calculator.CalculateCheapestPrice(130, PriceRangeDefinitions, out Total);
    
        // Show Proof of Concept
        String ProofOfConcept = String.Empty;
        for (int i = 0; i < Packages.Count; i++)
        {
            ProofOfConcept += Packages[i].AmountOfLicenses.ToString() + " Licenses " +
                Packages[i].PriceRange.PricePerLicense.ToString() + "$ each" + Environment.NewLine;
        }
        ProofOfConcept += Environment.NewLine + "TOTAL: " + Total.ToString();
    
        MessageBox.Show(ProofOfConcept);
    }
    

    【讨论】:

    【解决方案5】:

    可能是 KeyValuePair 集合或字典?

    【讨论】:

      猜你喜欢
      • 2015-09-22
      • 2019-01-13
      • 2016-02-29
      • 1970-01-01
      • 2017-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-14
      相关资源
      最近更新 更多