Алгоритм для процента, не зная общее количество


17

Предположим, есть nлинии для горячей линии.

Всякий раз, когда клиент звонит на горячую линию, звонок переадресовывается на одну из nлиний. И я хочу назначить процент звонков для каждой из n строк. Предположим, что есть две линии, и одна линия назначена на 60%, а другая - на 40%, общее количество звонков - 10, поэтому первая линия получит 6 звонков, а вторая получит 4 звонка.

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

Как я могу распределить количество звонков, не зная общее количество звонков?


2
После того, как по линии 1 поступит 6 звонков, наберите по 2 звонка по линии 2. То есть не заботятся о фактическом общем количестве, заботятся о распределении по «периоду» (в нашем случае 10), который вы знаете. Очевидно, что вы можете делать такие вещи, как альтернативные строки, кроме последнего значения, поэтому нет необходимости в строгом ожидании. Если есть какая-то очередь, делайте процент на основе текущих строк в очереди.
Заводная муза

что такое "звездочка" и "DID"?
комнат

@ Clockwork-Muse: я бы предложил округлять здесь целые числа вместо того, чтобы поддерживать распределение 6-4. В противном случае ваш дистрибутив будет отключен, если вы не знаете, что у вас есть точное кратное 10 вызовов. Например, если поступит всего 6 вызовов, предложенный вами подход назначит их всем строке A; тогда как более правильный подход будет 4 к A и 2 к B (назначается в порядке ABAABA, если вы округляете целые итоговые значения для каждой строки)
Flater

Ответы:


26

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

Например: после назначения первого вызова на линию 1:

 total calls line1      total calls line2    perc.line 1    perc. line 2
 1                      0                    100%             0% 
                                             *above 60%      *below 40% <- next call to 2
 1                      1                    50%             50% 
                                             * below 60%:    *above40% next to line1
 2                      1                    66%             33%
                                             *above 60%      *below 40% <- next to line 2
 2                      2                    50%             50% 
                                             * below 60%:    *above40% next to line1
 3                      2                    60%             40% 
                                             * both hit the mark: next call arbitrary
 4                      2                    66%             33%
                                             *above 60%      *below 40% <- next to line 2
 4                      3                    57.1%             42.85%
                                             *below 60%      *above 40% <- next to line 1

...

РЕДАКТИРОВАТЬ: этот подход может быть улучшен не путем использования абсолютной разницы, а путем выбора линии, которая минимизирует сумму квадратов всех отклонений. Это также даст вам лучший результат, если вы точно достигнете целевых значений.


2
Возможно, вы захотите изменить это «до тех пор, пока» на более явное «использовать другую ссылку» - FWIW.
DougM

@DougM: см. Мое редактирование.
Док Браун

5
  • Давайте предположим, что число работников составляет менее 100
  • Создать массив рабочих вместимостью 100
  • Поместите в этот массив работника число раз, равное проценту вызовов, которые он должен получить, например, если worker1 должен получить 30% всех вызовов, затем поместите его в позиции от 0 до 29 массива.
  • В конце должна использоваться каждая позиция массива, и рабочие должны появляться в массиве столько раз, сколько процент вызовов они должны получить.
  • В цикле сгенерируйте случайное число от 0 до 99 и назначьте входящий вызов рабочему в этой позиции массива. Если работник занят, повторите.
  • Таким образом, по чистой вероятности, звонки будут распределяться по желанию
  • В моем примере worker1 имеет 30/100 шансов на выбор на любой итерации.

4

Я согласен с решением @ DocBrown. Поместив его в форму алгоритма:

for each incoming call:
    sort lines ascending by delta* (see footnote below)

    // first element in array gets the call 
    increase number of calls for first element by 1
  • Дельта определяется фактическим процентом минус ожидаемый процент строки. Таким образом, те, кто имеет наибольшую отрицательную дельту, являются теми, которые больше всего требуют вызова, чтобы соответствовать ожидаемому проценту.

    Например, в случае, когда ожидаемые проценты для строк 1 и 2 составляют соответственно 60% и 40%, а их фактические проценты составляют 50% и 50%, вы увидите строку заказа 1, за которой следует строка 2, так как -10 % меньше 10%. Следовательно, линия 1 получит вызов.

    Я настоятельно рекомендую использовать сортировку вставкой, поскольку она работает лучше всего, когда массив уже отсортирован.

Кроме того, в качестве незначительной оптимизации, если вы отслеживаете общее количество вызовов до сих пор, вместо того, чтобы вычислять фактический процент каждой строки, вы можете просто рассчитать общее количество вызовов для этой линии минус ожидаемый процент для этой линии. строка умножает на общее количество вызовов (delta = t_i - p_i * T). В этом случае дельта представляет собой просто отрицательное количество вызовов для достижения ожидаемого процента.

Я надеюсь, что это проясняет любые другие сомнения.


спасибо @Neil, вы действительно помогли мне, но когда оба попадают в цель, какую линию мне следует назвать, есть ли какие-то критерии для этого.
Акю

@akku С моим алгоритмом вы всегда берете первое всегда после сортировки, то есть алгоритму все равно. Если у вас есть другой критерий, вы должны сделать так, чтобы он весил соответственно при сортировке. Другими словами, если номер строки был важен, то вы должны взять дельту, умножить на общее количество строк, а затем добавить текущий номер строки. Это благоприятствует более высоким номерам строк, предполагая, что все остальное равно.
Нил

@Neil: ваш ответ в порядке, но всякий раз, когда я вижу, что кто-то предлагает полностью отсортировать массив, чтобы найти минимальное значение, я думаю: «Великий Скотт, это действительно необходимо?»
Док Браун

@DocBrown O(n)- это то, что вы можете ожидать, сортируя уже отсортированный список с сортировкой вставкой, и O(n)это то, что вам нужно использовать, чтобы найти наименьшее значение. Я просто предполагаю, что это отсортировано.
Нил

@Neil: только потому, что оба алгоритма являются O (n), они не одинаково просты (или одинаково быстры).
Док Браун

2

Предположения, как указано в ОП

  1. Число строк n известно и;
  2. % Каждой строки известен

Разработка алгоритма

  1. Определите каждую строку по ее%

  2. Сортируйте каждую строку по ее положению от 0, определенного как (текущий% работников - назначенный% работников), или по случайному назначению, если все строки = 0

  3. Переадресация каждого звонка на наибольшую линию от 0

Пример: 3 строки с% 20, 30 и 50 соответственно. В момент времени x звонит 1 человек, и, поскольку каждая линия находится на расстоянии 0 от 0, она назначается случайным образом - скажем, на линию 2, которая должна удерживать 30% всех вызовов. Поскольку линия 2 должна удерживать 30% всех вызовов и теперь удерживает 100% всех вызовов, ее позиция с 0 увеличивается. Следующий вызывающий теперь будет назначен либо на строку 1, либо на строку 3 и т. Д. До равновесия (0), и, таким образом, цикл повторяется.


0

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

код псевдо ...

int running_total_of_calls = 0

//This is hard coded for clarity. You'd most likely want to dynamically populate this array depending and probably distribute the work by alternating workers. Notice how "worker1" appears 6 out of 10 times in the array.
string[] worker = new string[10]
workers[0] = "worker1"
workers[1] = "worker1"
workers[2] = "worker1"
workers[3] = "worker1"
workers[4] = "worker1"
workers[5] = "worker1"
workers[6] = "worker2"
workers[7] = "worker2"
workers[8] = "worker2"
workers[9] = "worker2"

while(1) //run forever
    //This is where the distribution occurs. 
    //first iteration: 0 modulus 10 = 0. 
    //second: 1 modulus 10 = 1
    //third: 2 modulus 10 = 2
    //...
    //10th: 10 modulus 10 = 0
    //11th: 11 modulus 10 = 1 
    //12th: 12 modulus 10 = 2
    //...
    int assigned_number = running_total_of_calls % workers.Count //count of workers array
    string assigned_worker = workers[assigned_number]
    do_work(assigned_worker)
    running_total_of_calls = ++running_total_of_calls
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.