如何将一个数字转换为价格范围

3

我希望计算客户购买我的产品许可证时的收费金额。

我按许可证范围出售:

  • 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),我得到了我一直在寻找的答案。
谢谢大家。

2
您不允许使用136个许可证。 - zaf
2
你会为70个许可证收取多少费用?(50 * $20)+(20 * $40)= $1800,或者(49 * $20)+(21 * $30)= $1610? - Mark Byers
1
为什么不简化您的定价,使其成为每个用户的单一费率,取决于购买的许可证数量? - parkr
Jon,我按照你的建议进行了编辑。 - Anon1865
2
您可以以660美元获得16个许可证,或以620美元获得31个许可证——节省40美元并且放弃15个许可证。这是一个非常有趣的方案。 - Jeffrey L Whitledge
显示剩余10条评论
5个回答

2
如果给出这样的价格结构,我会认为以客户的最佳利益为出发点,应该通过购买最适合自己需求的套餐来尽量降低成本。下面的算法使用动态规划来计算购买特定数量许可证的最小可能价格(虽然我没有实现多买少买可以省钱的功能):
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,可以通过购买35个许可证的2个块来获得。您正在建议一种贪婪算法。这会使您的客户感到困惑。聪明的客户将分两个订单而不是一个大订单,并节省$400。
我建议更改您的价格,以便在每个许可证为$20时,购买的许可证数量没有上限。

马克,我也理解你关于贪心算法的观点。我之前是从Fogbugz上复制的,但现在想想你说得对。顺便说一下,由于软件运行在单个服务器上,客户无法合并许可证。 - Anon1865
马克,我测试了算法。如果你放11个许可证,它会给你440美元,这是错误的;应该是540美元。在你创建的数组中,前10个元素是正确的,第11个是错误的。 - Anon1865
@Anon1865:为什么你不能以每个40美元的价格购买11个许可证?那么你的例子是错误的吗? - Mark Byers
2
从商业角度来看,我不理解批处理的概念。如果你在卖番茄,批次是有意义的,因为发送一辆装载量固定的卡车会有固定成本。但对于软件而言,不存在这样的成本:你的目标是一次性销售尽可能多的许可证。对我来说,不断降低的单价是有意义的。 - Mathias
1
@Anon1865 是的,但是我认为支持成本取决于许可证的总数,而不是分批次的方式。重要的是您需要支持60个许可证,而不是50个许可证+10个许可证。 - Mathias
显示剩余2条评论

0

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

代码可能看起来像这样:

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

0

我为您制作了一个计算类...更加以客户为导向的。 它可以根据您定义的价格范围,计算出最便宜的价格。

示例: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);
}

Steav,谢谢你的努力,但这就是我所称之为过度工程的东西...请查看https://dev59.com/Qk3Sa4cB1Zd3GeqPuFO0上的答案以了解多么简单。 - Anon1865

0
如果我是需要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,该计划来源于http://www.fogcreek.com/FogBugz/PriceList.html。看起来对Joel有用... - Anon1865
Herbert,非常好的评论。在您的示例中,我考虑向客户收取50到60个许可证之间的差额,以购买额外的10个许可证。 - Anon1865
它可能有效并不意味着它是最优的。如果我是一个之前购买了50个许可证的客户,现在回来说我想再买10个,我希望你能考虑到我已经是批量购买者,并且期望得到更好的价格。这种根据购买数量而降低价格(或另一种名为“固定价格-按任意数量分组”的方式)的优势对供应商或客户来说是什么?我认为我看到了小型供应商的优势:无需跟踪客户的先前购买记录。作为客户,我不喜欢这样。 - Herbert Sitz

-1
一个KeyValuePair集合或者字典可能是个不错的选择?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接