Вычисление, какой пункт является следующим в процентном распределении - PullRequest
3 голосов
/ 03 сентября 2010

Я работаю над проектом, который включает в себя переадресацию телефонных звонков на несколько направлений.

Например, я хочу:

  • 10% вызовов, чтобы перейти к месту назначенияA
  • 20% звонков для перехода в пункт назначения B
  • 30% звонков для перехода в пункт назначения C
  • 40% вызовов для перехода в пункт назначения D

Количество мест назначения и их проценты должны быть настраиваемыми.


Я думал о том, как это сделать, поиграв с электронными таблицами и некоторым кодом, и я придумал это:

Для каждого пункта назначения возьмите случайное число, умножьте его на процент и выберите пункт назначения с наибольшим номером.Вот так:

Item: RANDOM * PERCENTAGE = RESULT
   A:   48   *     10     =   480
   B:   33   *     20     =   660
   C:   81   *     30     =  2430  <--- Highest number, select C
   D:    5   *     40     =   200

Я думал, что сработаю, так как D будет явно выбран больше всего, затем C, затем B и меньше всего A.

Но этоне работаетЕсли я делаю это 5000 раз и вычисляю фактический процент раз, когда был выбран каждый пункт назначения, я получаю следующее:

  • 1% вызовов, направленных на пункт назначения A
  • 12% отзвонки, чтобы перейти к месту назначения B
  • 31% звонков, чтобы перейти к месту назначения C
  • 56% звонков, чтобы перейти к месту назначения D

Воткод, который я использовал для проверки этого:

// Initialise item weighting percentages
Dictionary<string, int> weighting = new Dictionary<string, int>();
weighting["A"] = 10; //10%
weighting["B"] = 20; //20%
weighting["C"] = 30; //30%
weighting["D"] = 40; //40% (total = 100%)

// Initialise data set used for each iteration
Dictionary<string, int> data = new Dictionary<string, int>();

// Initialise counts of the selected items
Dictionary<string, int> count = new Dictionary<string, int>();
count["A"] = 0;
count["B"] = 0;
count["C"] = 0;
count["D"] = 0;

Random rand = new Random();

// Loop 5000 times
for (int i = 0; i < 5000; i++) {

    // For each item, get a random number between 0 and 99
    // and multiply it by the percentage to get a
    // weighted random number.
    data["A"] = rand.Next(100) * weighting["A"];
    data["B"] = rand.Next(100) * weighting["B"];
    data["C"] = rand.Next(100) * weighting["C"];
    data["D"] = rand.Next(100) * weighting["D"];

    // Find which item came out on top and increment the count
    string sel = data.First(x => x.Value == data.Max(y => y.Value)).Key;
    count[sel]++;

    // Log, so you can see whats going on...
    if (i < 15)
        Console.WriteLine("A:{0:00000}  B:{1:00000}  C:{2:00000}  D:{3:00000}  SELECTED:{4}",
            data["A"], data["B"], data["C"], data["D"], sel);
    else if (i == 15) Console.WriteLine("...");

}

// Output the results, showing the percentage of the number
// occurrances of each item.
Console.WriteLine();
Console.WriteLine("Results: ");
Console.WriteLine("    A = {0}%", 100 * ((double)count["A"] / (double)count.Sum(z => z.Value)));
Console.WriteLine("    B = {0}%", 100 * ((double)count["B"] / (double)count.Sum(z => z.Value)));
Console.WriteLine("    C = {0}%", 100 * ((double)count["C"] / (double)count.Sum(z => z.Value)));
Console.WriteLine("    D = {0}%", 100 * ((double)count["D"] / (double)count.Sum(z => z.Value)));

результаты :

A:00780  B:00300  C:01740  D:03680  SELECTED:D
A:00600  B:00660  C:00060  D:03400  SELECTED:D
A:00900  B:01880  C:00510  D:00720  SELECTED:B
A:00260  B:01380  C:00540  D:01520  SELECTED:D
A:00220  B:01960  C:00210  D:02080  SELECTED:D
A:00020  B:01400  C:01530  D:00120  SELECTED:C
A:00980  B:00400  C:01560  D:03280  SELECTED:D
A:00330  B:00300  C:01500  D:03680  SELECTED:D
A:00590  B:00460  C:02730  D:02400  SELECTED:C
A:00580  B:01900  C:02040  D:01320  SELECTED:C
A:00620  B:01320  C:00750  D:01760  SELECTED:D
A:00320  B:01040  C:01350  D:03640  SELECTED:D
A:00340  B:01520  C:02010  D:03880  SELECTED:D
A:00850  B:01420  C:00480  D:03400  SELECTED:D
A:00560  B:00680  C:00030  D:00000  SELECTED:B
...

Results: 
    A = 1.44%
    B = 11.54%
    C = 30.6%
    D = 56.42%

Может кто-нибудь предложить способ исправить это, чтобы получить реальные процентыкак настроено?


А для бонусных баллов кто-нибудь может предложить способ сделать что-то подобное, но не используя случайные числа, чтобы последовательность выбранных пунктов назначения была четко определена.Использование приведенного выше примера будет выводить эту последовательность каждый раз:

ABCDBCDCDD ABCDBCDCDD ABCDBCDCDD ABCDBCDCDD ...

(обратите внимание, что последовательность распределена равномерно)

Спасибо.Бен

Ответы [ 4 ]

5 голосов
/ 03 сентября 2010

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

Вам нужно представить, что линия отрисовывается на странице, проходящей от 0 до 100Теперь мы делим эту линию пропорционально между вашими пунктами назначения.Затем мы используем случайные числа, чтобы выбрать точку на этой линии.Пункт назначения, у которого есть эта область линии, является выбранным.

РЕДАКТИРОВАТЬ: Попытка на линейной диаграмме

|-----------------------------------------------------|   Line 1 to 100
|-----|----------|---------------|--------------------|   Line split proportionally
0  A  10    B    30     C        60      D           100

Мы можем сделать это следующим образом.

Предположим, что проценты вашего назначения в массиве, а не в отдельных переменных.

int totalPercentages = 0; 
int destinationsIndex = -1;
int randomNumberBetween0and100 = GetRandomNumber();
for(int i = 0; i < destinationPercentageArrays.Length; i++)
{
    totalPercentages += destinationPercentageArrays[i];
    if (totalPercentages > randomNumberBetween0and100)
    {
        destinationIndex = i;
        break;
    }
}

if (destinationIndex == -1)
{
   throw new Exception("Something went badly wrong.");
}

Теперь переменная destinationIndex указывает на выбранный пункт назначения.

2 голосов
/ 03 сентября 2010

Для распределения по процентам, которые вы дали, сделайте следующее:

Создайте случайное число от 1 до 100 (включительно)

If < 10 A
If > 10 < 30 B
If > 30 < 60 C
If > 60 D

Что касается вопроса о том, какиметь определенный список, просто упорядочить места назначения в массив и перечислять их по одному.Когда вы закончите, начните снова с начала.

string[] destinations = new string[] { "A", "B", "C", "D", ... }

int counter = 0;

//when need routing
RouteTo(destinations[counter]);
counter++;
if (counter == destinations.Length)
{
     counter = 0;
}
1 голос
/ 03 сентября 2010

Другая возможность - заполнить список размером 100, используя цикл for и вставляя каждое значение, умноженное на его вес. Затем случайным образом выберите элемент списка.

Пример, короткий список (10 наименований)

  • 5x A
  • 4x B
  • 1x C

Список = {A, A, A, A, A, B, B, B, B, C}

Случайное число от 0 до 9.

0 голосов
/ 03 сентября 2010

Это создаст случайный список длиной 100 символов, т.е. ABCDBCDCDD ...

    static void Main()
    {
        var weighting = new Dictionary<char, int>();
        weighting['A'] = 10; //10%
        weighting['B'] = 20; //20%
        weighting['C'] = 30; //30%
        weighting['D'] = 40; //40% (total = 100%)

        var test = CreateOrder(weighting);
    }

    static IEnumerable<char> CreateOrder(Dictionary<char, int> weighting)
    {
        var list = new List<KeyValuePair<int, char>>();
        var random = new Random();
        foreach (var i in weighting)
        {
            for (int j = 0; j < i.Value; j++)
            {
                list.Add(new KeyValuePair<int, char>(random.Next(), i.Key));
            }
        }
        return list.OrderBy(u=>u.Key).Select(u => u.Value);
    }
...