spamsink: (Default)
[personal profile] spamsink


Уж как переложил на бытовые реалии, так переложил.

Имеется поликлиника, в которых работают специалисты N разных профилей. Приходящие пациенты встают в общую очередь. Сами они объяснить, какой врач им нужен, не могут, а в ответ на вопрос "К такому-то есть кто-нибудь?" из очереди выходит первый нуждающийся в этом враче, или никто не выходит, если таких на данный момент нет. Время, затрачиваемое регистратурой на один вопрос, в обоих случаях одинаково.

Тривиальный принцип работы регистратуры - называть всех специалистов по кругу, но при "эпидемии" это очевидным образом неэффективно.

Требуется придумать адаптивный алгоритм, стремящийся максимизировать пропускную способность регистратуры, в т.ч. и в условиях изменения характера "эпидемий", сохраняя справедливость: стоящий первым в очереди никогда не должен ждать дольше, чем M вопросов.



Начинаем с колоды из N карточек с номерами от 1 до N. Далее играем раунды так:

Выкликаем согласно номерам на карточках, и раскладываем карточки в N пар кучек, в каждой паре справа - успешные вызовы (Si), слева - неудачные (Fi).


Когда колода исчерпалась, смотрим: если во всех N парах было хотя бы по одному успешному вызову (все Si > 0), то набор Si более или менее неплохо моделирует соотношение частот клиентов врачей, а для уточнения соотношения на следующем круге составляем колоду из 2*Si карточек каждого типа, если это не больше, чем M, иначе по Si каждого типа.

Если же оказалось, что какие-то Si равны нулю, то это значит, что частота запросов для этих i меньше, чем 1/K, где K - размер колоды на только что закончившемся круге. Тогда составляем колоду из min(M, 2*K) карточек так, чтобы в них было по одной карточке для тех i, для которых Si было = 0, а остальные были в той же пропорции, что и ненулевые Si.

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

Споласкиваем, повторяем.

Date: 2013-06-14 01:58 am (UTC)
From: [identity profile] vgramagin.livejournal.com
При M=N решение очевидно!

Date: 2013-06-14 02:15 am (UTC)
From: [identity profile] vgramagin.livejournal.com
Может, наоборот?

Date: 2013-06-14 04:02 am (UTC)
From: [identity profile] morfizm.livejournal.com
Если наоборот, то при определённом раскладе имён врачей и больных нельзя построить никакого алгоритма, чтобы гарантировать меньше M вопросов. Не говоря уже об оптимальном, учитывающем эпидемии и пр.

Date: 2013-06-14 01:08 pm (UTC)
From: [identity profile] vgramagin.livejournal.com
Или я не понял условия задачи, или вы.

Пусть N=10, M=20. Очевидно, что если регистратор будет просто задавать вопросы "кто к врачу 1, кто к врачу 2,..., кто к врачу 10" - то первый в очереди к любому врачу не будет ждать дольше 10 вопросов, что, очевидно, меньше 20

Date: 2013-06-14 03:00 am (UTC)
From: [identity profile] galkao.livejournal.com
Называем специалиста 1. Если есть желающий к нему, то снова вызываем специалиста 1. Если есть желающий - снова, и так - до тех пор, пока количество вызовов не превышает M/N. Далее - специалист 2. Ну и по кругу.

Проще говоря, допустим, M/N=10, тогда 10 раз подряд называем каждого специалиста по очереди (либо, если в какой-то момент не оказалось желающего, переходим к следующему специалисту).

Date: 2013-06-14 03:12 pm (UTC)
From: [identity profile] galkao.livejournal.com
Но тогда получится более сложный алгоритм, который в регистратуре ни запомнить, ни воплотить не смогут:-)

То есть нужно хранить коэффициент для каждого специалиста. Если число желающих совпало с M/N, добавить к коэффициенту "свободные вызовы", если число желающих меньше, убавить коэффициент до количества вызовов (а "остаток" добавить к "свободным").

Опять пример: M/N=10.
Вызываем 1-го, желающих - 3. Коэффициент К1==3. Свободный увеличиваем на 7 (изначально он равен 0).
Вызываем 2-го, желающих - 7, К2==7, к свободным добавляем 3.
Вызываем 3-го, желающих - 10, по одному изымаем из свободных дополнительные вызовы до тех пор, пока либо не кончатся желающие, либо кончатся вызовы.
Вызываем 4-го, желающих - 0, К4==1 (1- минимальное значение коэффициента). К свободным добавляем 9.

Ну и далее по кругу... Если вдруг предполагается несколько эпидемий, то из свободных на каждом этапе нельзя выбирать все вызовы, а только половину, например.

Date: 2013-06-14 04:30 am (UTC)
From: [identity profile] morfizm.livejournal.com
Пусть freshness(k) - сколько шагов назад назывался k-й врач.
Пусть popularity(k) - сколько пациентов к k-му врачу стоит в очереди (допустим какой-то оракул знает ответ).
Сортируем врачей по лексикографическому возрастанию тупла (max(M-a(i), N), -popularity(k)), и выбираем первого.

Этот алгоритм будет эффективно приоритезировать: самый популярный врач будет обслуживаться максимально, за исключением тех врачей, которых надо назвать для соблюдения справедливости. При одинаковой популярности, трафик будет делиться поровну между всеми одинаковыми.

Допустим, мы не знаем, чему равен popularity(k). Эту популярность надо *измерить*.

Измерять её можно по-разному. Вижу две крайности:

1. "Мочить" одного врача до упора, пока не кончатся его клиенты, или пока его не вытеснят ребята, которые по справедливости. Грубо говоря, popularity(k) = 1 если на предыдущем шаге был хотя бы один клиент, иначе 0. Кстати, в этом случае если к ребятам по справедливости кто-то пришёл, то в следующем раунде они уже будут соревноваться с первым и делить с ним трафик. Но те их них, которые "кончатся", отвалятся из соревнования. В этом случае у нас такой расклад: для лузеров, к которым никого нет, будет задан один вопрос на каждые M шагов. Для тех, к кому за M-шаговый цикл пришло 5 человек, будет 5 или 6 вопросов (в зависимости от того, люди исчерпались до или после обработки лузеров).

Можно делать какую-то адаптивную политику и учитывать историю, например, не присваивать единицу в популярность, а прибавлять единицу, а в случае неудачи не обнулять, а вычитать или делить. Если мы знаем паттерны эпидемий, можно подтюнить формулы так, чтобы было счастье.

2. Можно давать каждому врачу вплоть до M/N попыток и запоминать количество успешных. Круг может закончиться раньше, чем через M шагов, если к кому-то было менее M/N пациентов. На второму круге эти числа уже можно использовать как популярность. Время от времени делать тюнинг, заново пересчитывая. Или учитывая длины новых отрезков в формулу популярности. В случае одного доминирующего и остальных отдыхающих, его throughput будет заметно меньше, т.к. он будет получать не (M-N)/M, а (M/N)/(M/N + N). Зато будет некоторый QoS, честнее пропорция трафика и быстрее реакция на появление новых популярных врачей.

За неимением дополнительных знаний об эпидемиологических свойствах, я бы выкатил в продакшн п.1., ибо это максимизирует max throughput для лучшего, что есть нередко важнейшая метрика.

Date: 2013-06-14 04:56 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Не понимаю вопрос про эффективную реализацию тюнинга. Это, типа, дальнейшее усложнение, нам важно не только оптимизировать вопросы регистратуры, но и оптимизировать вычисления? Требуется понять каков относительный вес этих вещей, и в чём измерять эффективность реализации тюнинга.

Ещё вопрос: *зачем* минимизировать количество магических чисел в параметрах?

Ещё вопрос: вы, с одной стороны, говорите, что 50% лишних вопросов это плохо, а, с другой, намекаете, что п.2. лучше, чем п.1., когда при определённом паттерне входящих, п.1. будет производить намного меньше лишних вопросов, чем п.2. Т.е. вы оцениваете, держа в голове определённый паттерн. Каков он? Для каких данных мы оптимизируем?

Date: 2013-06-14 05:17 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Сильно смещённое - это понятно, а что такое равномерное? Скажем так, интересно было бы знать, каково ожидаемое отношение K/N специалистов, могущих получить клиента (K) ко всем специалистам (N) в случае вот этой самой равномерности.

Date: 2013-06-18 07:23 am (UTC)
From: [identity profile] morfizm.livejournal.com
Я не очень понял связь между "соотношение K/N близкое к единице" и "клиенты приходят с близкими средними частотами", мне это видится как два различных независимых параметра.

Клиенты могут приходить с теми же самыми близкими средними частотами, но вдвое реже, и K/N упадёт вдвое.

Date: 2013-06-18 04:24 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Только что посмотрел апдейт.

Мне надо ещё подумать, чтобы убедить себя, что это работоспособный алгоритм. Потенциально слабое место мне пока видится вот здесь: вероятности обновляются *только* когда 2*Si > M, соответственно, если мы имеем ситуацию, в которой это соотношение consistently не выполняется (например, уже есть эпидемия к 1-2 врачам), то про нового, третьего врача, к которому возникла эпидемия, регистратура никогда не узнает (в смысле, не начнёт увеличивать его вероятности), пока не кончится предыдущая эпидемия. Правильно ли я понял?

Вообще, после всех этих уточнений мне эта задача уже почти не нравится. Она слишком размыта в изначальной формулировке, и слишком сложна (и неуниверсальна) в деталях специфики условия, после того, как вы поделились этими деталями.
Edited Date: 2013-06-18 04:25 pm (UTC)

Date: 2013-06-18 04:29 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Вы лучше выложите ваши тестовые данные (+ приберегите ещё похожих данных для проверки), и мы посоревнуемся в реализации.

Date: 2013-06-19 07:57 pm (UTC)
From: [identity profile] morfizm.livejournal.com
Жаль.
Но вы понимаете, что существуют исходные данные + детали критериев оценки, на которых моё решение будет лучше вашего? Если же вы допилите условия до таких, на которых ваше решение будет лучше, то вы, по сути, опишите ваш test data set. Я поэтому и подумал, почему бы не оптимизировать этот процесс и не выложить test data set as is.

Date: 2013-06-14 04:40 am (UTC)
From: [identity profile] yatur.livejournal.com
А какая программистская ситуация соответствует такой схеме? Чтобы пациент мог ответить на вопрос "вы к окулисту?", но не мог бы ответить на вопрос "к какому вы врачу"? Ну, т.е., человек может забыть как называется глазной врач ("окулист" - пассивное слово), но с программами этого ведь обычно не происходит?

Date: 2013-06-14 04:45 am (UTC)
From: [identity profile] morfizm.livejournal.com
Я думаю, для простоты модели можно считать, что "N разных профилей ~= N врачей с такими-то именами", т.к. несколько врачей одного профиля можно легко сгруппировать в "одного врача" (назвав его "группа окулистов", скажем), и свести задачу к предыдущей.

Date: 2013-06-14 05:46 am (UTC)
ak_47: (default)
From: [personal profile] ak_47
Например, простой thread scheduling вполне подходит условиям задачи.

Date: 2013-06-14 03:52 pm (UTC)
From: [identity profile] sab123.livejournal.com
Тогда это неправильная аналогия :-) Это получается несколько отдельных очередей, из которых регистратура вызывает по одному.

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

Date: 2013-06-14 04:07 pm (UTC)
From: [identity profile] sab123.livejournal.com
Можно аналогичную информацию извлечь эвристически.

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

Эту логику можно расширить на хранение результатов последних N опросов. И делать специальный круг для очередей, которые вернули непусто при последних N опросах, потом при как минимум последних (именно последних, а не с перерывами в истории) N-1 опросах, как минимум N-2, и так далее. Естественно, что как только при каком-то опросе все возвращают "пусто", память сбрасывается.

Date: 2013-06-14 04:33 pm (UTC)
From: [identity profile] sab123.livejournal.com
Счетчик на каждую очередь, в котором считать количество последних непрерывных успешных попыток? Если очень захотеть, можно это даже держать не в виде массива счетчиков с индексированием по номеру очереди, а пары (номер очереди, счетчик), отсортированные в массиве по значению счетчика. Сортировка будет относительно легко поддерживаться при каждом проходе.

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

Date: 2013-06-14 04:41 am (UTC)
From: [identity profile] morfizm.livejournal.com
Ещё - интуитивно кажется, что для реальных задач может хорошо подойти алгоритм на основе вышеуказанного, но в котором есть следующие дополнения:
1. У каждого врача есть лимит на количество вопросов подряд, вначале limit(k) = 2.
2. В случае упирания в limit после цепочки из q вопросов, происходит следующее: если все q были обработаны, то limit := limit * 2 (по сути он будет 2q), а popularity := q. Если же цепочка закончилась в результате облома, и на последний вопрос никто не откликнулся, сбрасываем в начало: limit := 2, popularity := 0.

Получаем ситуацию, в которой у врача-лузера есть шанс постепенно "разогнаться" и потом делить канал почти поровну с предыдущим победителем, пока один из них не исчерпает спрос.

Date: 2013-06-14 05:11 am (UTC)
From: [identity profile] morfizm.livejournal.com
О... понятно, сортировку надо подтюнить - вместо max(M-a(i), N) делать что-то вроде max(M-N-a(i), N). Чтобы в случае когда осталось M-2*N шагов, уже начинать сортировать оставшихся, т.к. каждому лузеру полагается до 2 попыток.

P.S. Я там где-то мог не учесть +1 или -1, это в юнит тестах отлаживается :)

Date: 2013-06-14 06:09 am (UTC)
From: [identity profile] vymenets.livejournal.com
Такое чувство, что условие неполное. Время приёма специалиста больше нуля? Больше или меньше времени на вопрос регистратуры? Может ли регистратура узнать, что конкретный специалист освободился?

Date: 2013-06-14 11:14 am (UTC)
From: [identity profile] morfizm.livejournal.com
Пусть автор меня поправит, но я думаю, что здесь имеется в виду, что время приёма у специалиста равно (или: не больше) времени вопроса. Скажем, регистратура задаёт вопрос раз в минуту, и этой минуты хватает, чтобы обслужить. Можно представить себе, что под именем специалиста скрывается целая группа похожих специалистов, которые позволяют такое быстрое обслуживание. Соответственно, перед каждым следующим вопросом все специалисты свободны, а работает только один из них одновременно. Но "оплата" идёт только за вопросы регистратуры. Каждый вопрос, даже если он вхолостую (никого из очереди к этому специалисту не оказалось), стоит минуту времени.

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

Date: 2013-06-14 01:24 pm (UTC)
From: [identity profile] dvv.livejournal.com
Ну чо, веса́ накидывать на используемых специалистов (с time decay). Логарифмически or something. Обычное дело…

Date: 2013-06-14 03:44 pm (UTC)
From: [identity profile] sab123.livejournal.com
Что-то тут не так. "Сами они объяснить, какой врач им нужен, не могут" не вяжется со способностью ответить на вопрос "К такому-то есть кто-нибудь?". Получается, что кто-то (регистратура?) должен им это объяснить перед тем, как поставить в очередь.

Решение - регистратура самообслуживания. Чтоб каждый пациент самостоятельно классифицировал, к какому врачу ему надо.

То есть, переходя на кнопкодавские реалии, вызывал функцию, назначающаю врача, в контексте своего собственного треда.

Profile

spamsink: (Default)
spamsink

February 2026

S M T W T F S
12345 67
8 91011 121314
15161718 192021
22 2324 25262728

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Mar. 5th, 2026 04:10 am
Powered by Dreamwidth Studios