2010-04-21 4 views
6

Я ищу алгоритм вычисления общей стоимости лицензий, приобретенных на основе схемы ценообразования «FogBugz для вашего сервера» (http://www.fogcreek.com/FogBugz/PriceList.html).Алгоритм для схемы ценообразования Fogbugz

FogBUGZ ценообразование:

  • 1 лицензия $ 299
  • 5 Пакет лицензий $ 999
  • 10 Пакет лицензий $ 1.899
  • 20 Пакет лицензий $ 3.499
  • 50 Пакет лицензий $ 7.999

Если вы спросите цитату, скажем, 136 лицензий, которые они вычисляют съел его как 22 694 доллара.

Как это сделать на C# или LINQ?

Любая помощь будет оценена по достоинству.

+0

В чем именно вам нужна помощь?Вы пытаетесь выяснить, как они выбирают подходящую схему ценообразования для значений, превышающих стандартные пакеты (51+ лицензий)? –

+3

Вид странного вопроса (или способ расспросить его с конкретной ссылкой на FogBugz ...), но я не уверен, что его нужно проголосовать вниз ... – mmacaulay

+2

См. [Предыдущий вопрос] OP (http: /stackoverflow.com/questions/2684261/how-to-convert-a-number-to-a-range-of-prices). –

ответ

7

Принятый ответ, в то время как элегантный кусок кода С точки зрения программиста, не дает наилучшую цену для клиента и, следовательно, не может быть элегантным решением с точки зрения клиента. Например, когда n = 4, принятый ответ дает 1196 долларов США, но клиент, очевидно, предпочел бы выбрать 5 лицензионных пакетов и заплатить всего 999 долларов вместо этого.

Возможно построить алгоритм, который может рассчитать минимальную возможную цену, которую клиент может заплатить, чтобы приобрести необходимое количество лицензий. Один из способов сделать это - использовать динамическое программирование. Я думаю, что что-то подобное может сделать трюк:

int calculatePrice(int n, Dictionary<int, int> prices) 
{ 

    int[] best = new int[n + prices.Keys.Max()]; 
    for (int i = 1; i < best.Length; ++i) 
    { 
     best[i] = int.MaxValue; 
     foreach (int amount in prices.Keys.Where(x => x <= i)) 
     { 
      best[i] = Math.Min(best[i], 
       best[i - amount] + prices[amount]); 
     } 
    } 
    return best.Skip(n).Min(); 
} 

void Run() 
{ 
    Dictionary<int, int> prices = new Dictionary<int, int> { 
     { 1, 299 }, 
     { 5, 999 }, 
     { 10, 1899 }, 
     { 20, 3499 }, 
     { 50, 7999 } 
    }; 

    Console.WriteLine(calculatePrice(136, prices)); 
    Console.WriteLine(calculatePrice(4, prices)); 
} 

Выход:

22694 
999 

Update Производить разбивку является немного более сложным, но я определенно думаю, что это будет полезно для ваших клиентов. Вы могли бы сделать что-то вроде этого (при условии печати на консоль, хотя реальная программа будет, вероятно, выход на вебе-страницу):

using System; 
using System.Linq; 
using System.Collections.Generic; 

class Program 
{ 
    static Dictionary<int, int> prices = new Dictionary<int, int> { 
      { 1, 299 }, 
      { 5, 999 }, 
      { 10, 1899 }, 
      { 20, 3499 }, 
      { 50, 7999 } 
    }; 

    class Bundle 
    { 
     public int Price; 
     public Dictionary<int, int> Licenses; 
    } 

    Bundle getBestBundle(int n, Dictionary<int, int> prices) 
    { 
     Bundle[] best = new Bundle[n + prices.Keys.Max()]; 
     best[0] = new Bundle 
     { 
      Price = 0, 
      Licenses = new Dictionary<int, int>() 
     }; 

     for (int i = 1; i < best.Length; ++i) 
     { 
      best[i] = null; 
      foreach (int amount in prices.Keys.Where(x => x <= i)) 
      { 
       Bundle bundle = new Bundle 
       { 
        Price = best[i - amount].Price + prices[amount], 
        Licenses = new Dictionary<int,int>(best[i - amount].Licenses) 
       }; 

       int count = 0; 
       bundle.Licenses.TryGetValue(amount, out count); 
       bundle.Licenses[amount] = count + 1; 

       if (best[i] == null || best[i].Price > bundle.Price) 
       { 
        best[i] = bundle; 
       } 
      } 
     } 
     return best.Skip(n).OrderBy(x => x.Price).First(); 
    } 

    void printBreakdown(Bundle bundle) 
    { 
     foreach (var kvp in bundle.Licenses) { 
      Console.WriteLine("{0,2} * {1,2} {2,-5} @ ${3,4} = ${4,6}", 
       kvp.Value, 
       kvp.Key, 
       kvp.Key == 1 ? "user" : "users", 
       prices[kvp.Key], 
       kvp.Value * prices[kvp.Key]); 
     } 

     int totalUsers = bundle.Licenses.Sum(kvp => kvp.Key * kvp.Value); 

     Console.WriteLine("-------------------------------"); 
     Console.WriteLine("{0,7} {1,-5}   ${2,6}", 
      totalUsers, 
      totalUsers == 1 ? "user" : "users", 
      bundle.Price); 
    } 

    void Run() 
    { 
     Console.WriteLine("n = 136"); 
     Console.WriteLine(); 
     printBreakdown(getBestBundle(136, prices)); 
     Console.WriteLine(); 
     Console.WriteLine(); 
     Console.WriteLine("n = 4"); 
     Console.WriteLine(); 
     printBreakdown(getBestBundle(4, prices)); 
    } 

    static void Main(string[] args) 
    { 
     new Program().Run(); 
    } 
} 

Выход:

n = 136 

2 * 50 users @ $7999 = $ 15998 
1 * 20 users @ $3499 = $ 3499 
1 * 10 users @ $1899 = $ 1899 
1 * 5 users @ $ 999 = $ 999 
1 * 1 user @ $ 299 = $ 299 
------------------------------- 
    136 users   $ 22694 


n = 4 

1 * 5 users @ $ 999 = $ 999 
------------------------------- 
     5 users   $ 999 
+0

Марк, это отличная идея ... Если бы я мог получить разбивку предлагаемого решения, чтобы я мог объяснить клиенту, почему дешевле для них , будет круто. – Anon1865

+0

@ Anon1865: Добавлен пример того, как вы могли бы дать клиенту пробой. Чтобы показать, почему это дешевле, вам нужно будет что-то сравнить. Вы можете создать аналогичную разбивку для другого алгоритма и показать, сколько они сохраняют, или, возможно, попытаться рекламировать, что вы даете им 5 лицензий вместо 4 без каких-либо дополнительных затрат. В любом случае, надеюсь, я дал вам достаточно, чтобы начать. –

+0

Это похоже на излишний. Я уверен, что простая жадная схема отлично подходит для этого ценового графика, если вы правильно установите отсечки. –

11
int licenses = 136; 
int sum = 0; 

while (licenses > 0) 
{ 
    if (licenses >= 50)  { sum += 7999; licenses -= 50; } 
    else if (licenses >= 20) { sum += 3499; licenses -= 20; } 
    else if (licenses >= 10) { sum += 1899; licenses -= 10; } 
    else if (licenses >= 5) { sum += 999; licenses -= 5; } 
    else      { sum += 299; licenses -= 1; } 
} 

// sum == 22694 

или

int licenses = 136; 
int sum = 7999 * Math.DivRem(licenses, 50, out licenses) 
     + 3499 * Math.DivRem(licenses, 20, out licenses) 
     + 1899 * Math.DivRem(licenses, 10, out licenses) 
     + 999 * Math.DivRem(licenses, 5, out licenses) 
     + 299 * licenses; 

// sum == 22694 
+0

Я просто пытался набрать аналогичный ответ. –

+1

+1, хотя алгоритм, основанный на разделении, будет более эффективным для тех клиентов, которые покупают шумихи лицензий;) –

+8

@ Kent Boogaart: ответ будет расширен, чтобы удовлетворить шутки лицензий. Измените 'int' на' long' для giga-squillions лицензий. – dtb

1

решением Марки является большим общим решением , и, безусловно, то, что вы должны пойти с (в случае, если цены когда-либо изменится.) Это решение сочетает в себе простоту DTB-х годов с корректность Марка:

int licenses = 136; 
int sum = 7999 * Math.DivRem(licenses, 50, out licenses) 
     + 7999 * Math.DivRem(licenses, 46, out licenses) 
     + 3499 * Math.DivRem(licenses, 20, out licenses) 
     + 1899 * Math.DivRem(licenses, 10, out licenses) 
     + 999 * Math.DivRem(licenses, 5, out licenses) 
     + 999 * Math.DivRem(licenses, 4, out licenses) 
     + 299 * licenses; 

похоже ONL y крайних случаев 5 лучше, чем 4, а 50 лучше, чем 46 ... 49. Хотя, реалистично, вы, вероятно, должны предлагать 50, когда кто-то ищет 45, так как дополнительные 5 лицензий стоят всего 2 доллара. Итак, может быть, в коде код 46-45.

Смежные вопросы