Раз в несколько лет возвращаюсь к задаче создания алгоритма для наиболее вероятного прогноза на основании ошибок предыдущих прогнозов. В этот раз попробую обойтись минимумом текста (ссылка на демо в конце).
Ссылка на оригинал статьи с объяснением принципа Доказательного Планирования (в оригинале Evidence Based Scheduling) — советую ознакомиться, чтоб быть в контексте.
-
Joel Spolsky Evidence Based Scheduling
Версия 1.0: Первый блин в коме (2019)
Как я видел решение этой задачи 5 лет назад (здесь очевидная ошибка): Берем самый худший вариант из предыдущего опыта и проецируем на текущую задачу — это и будет мой прогноз для Продукт Менеджера 😠.
Версия 2.0: Кажется, это успех (2024)
К примеру, у разработчика (исполнителя) есть несколько решенных задач с параметрами:
-
Дата начала выполнения задачи (start)
-
Дата его оценки финиша (estimation)
-
Дата реального финиша (finish)
Итак, добавлена новая задача, получен estimation от разраба. Теперь вопрос — насколько он ошибся?
Алгоритм расчета наиболее худшего сценария: Берем скорость самой неудачной задачи этого разработчика и вычисляем дату окончания текущей задачи.
💥 Алгоритм поиска наиболее вероятного сценария: Думаю, всем очевидно, что скорость «среднюю по больнице» брать — это неточно. Поэтому логика следующая: Человек работает, как правило, [плюс-минус] с одной скоростью — алгоритм должен ее «ощутить». Как именно: если среди возможных вариантов скоростей его работы встречаются два с минимальным отклонением — это повод искать ещё варианты с примерно таким же отклонением. В итоге, найденные варианты образуют «скопления», среднее значение которых можно учитывать как наиболее вероятное (поправьте в комментариях, если я неправ):
// NOTE: 1. Отсортированный список скоростей выполнения задач const sortedSpeeds: { v: number }[] = speeds.sort((e1, e2) => e1.v - e2.v) // NOTE: 2. Список отклонений между этими скоростями const deltas: { value: number | null; delta: number | null; prev: number | null; next: number | null; isSensed: boolean; // Это тот самый показатель "чувствительности" }[] = [] let minDelta = 1000000 let maxDelta = 0 for (let i = 0, max = sortedSpeeds.length; i < max; i++) { const prevValue = !!sortedSpeeds[i - 1] ? sortedSpeeds[i - 1] : null const nextValue = !!sortedSpeeds[i + 1] ? sortedSpeeds[i + 1] : null const currentValue = sortedSpeeds[i] const delta = typeof prevValue?.v === 'number' ? currentValue.v - (prevValue.v) : null deltas.push({ value: sortedSpeeds[i].v, delta, next: nextValue?.v || null, prev: prevValue?.v || null, isSensed: false, // by default }) if (typeof delta === 'number') { if (minDelta >= delta) minDelta = delta if (maxDelta <= delta) maxDelta = delta } } // NOTE: 3. Оставим только скорости с минимальным отклонением друг от друга // Коэфф sensibility нужен для большего охвата задач, когда их немного (я беру 4) // По мере роста задач коэфф. sensibility можно уменьшать до значения 1-2 const sensed: { counter: number; speedValues: number[]; averageSpeed: number; } = { counter: 0, speedValues: [], averageSpeed: 0, } for (let i = 0, max = deltas.length; i< max; i++) { if ((deltas[i].delta as number) <= sensibility * minDelta) { deltas[i].isSensed = true if (typeof deltas[i].value === 'number') { sensed.counter += 1 sensed.speedValues.push((deltas[i].value as number)) } } else deltas[i].isSensed = false } // NOTE: 4. Все! Теперь наиболее вероятная дата финиша // может быть расчитана в соответствии с наиболее вероятной скоростью: sensed.averageSpeed = getArithmeticalMean(sensed.speedValues)
ссылка на оригинал статьи https://habr.com/ru/articles/874226/
Добавить комментарий