Skip to content

Instantly share code, notes, and snippets.

@pavelpower
Last active August 2, 2023 14:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pavelpower/4c22b470f1e78030802963d61ae6b8e1 to your computer and use it in GitHub Desktop.
Save pavelpower/4c22b470f1e78030802963d61ae6b8e1 to your computer and use it in GitHub Desktop.
Метод-Монте Карло для расчета вероятности срока решения проекта на основе пропускной способности по задачам и количеству задач в проекте. В модель добавлена возможное наступление рисков, которые могут добавить или убавить количество задач к проекту
// Данные по пропускной способности по итерациям времени
// итерация - минимальная единица времени в которой хотим считать
// длительность проекта
const historicalDataThroughput = [
4, 1, 2, 1, 4, 3, 2, 3, 2, 3, 6,
3, 2, 1, 6, 13, 3, 1, 6, 1, 2,
4, 4, 6, 3, 1, 3, 3, 6, 6, 2,
6, 2, 1
];
// количество задач в проекте
const countTaskInProject = 40;
// Риски которые могут случится с какой-то вероятностью
// И добавить новых задач к проекту
// Предполагаем что риск может сработать один раз
const risks = [
// С вероятностью "probability" наступления риска, к проекту добавится от "min" до "max" задач
{ probability: 2, countTask: { min: 10, max: 15 } },
{ probability: 12, countTask: { min: 2, max: 20 } },
{ probability: 5, countTask: { min: 5, max: 12 } },
{ probability: 4, countTask: { min: -8, max: -2 } },
]
// Выполнить функцию Монте-Карло и получить результат
const result = MonteCarloForProject(
historicalDataThroughput,
countTaskInProject,
risks,
10000
);
// Показать результат в консоли
console.table(result);
/**
* Получить распределение вероятности завершения проекта
* на основе количества решаемых задач за итерацию
*
* @param {Array<Number>} historicalDataThroughput Набор испторических данных по пропускной способности
* @param {Number} countTaskInProject Количество задач в проекте
* @param {Array<{probability: Number, countNewTasks: Number}>} risks Риски
* @param {Number} numberOfExperiments Количество проводимх эксперимантов
* @returns {Array<Number, Number>} key - количество фич в итерации, value - частота
*/
function MonteCarloForProject(
historicalDataThroughput,
countTaskInProject,
risks,
numberOfExperiments
) {
const result = new Map();
const len = historicalDataThroughput.length
let experiment = 1;
for (; experiment <= numberOfExperiments; experiment++) {
let prjTasks = countTaskInProject;
// Перебираем все риски
risks.forEach(r => {
let randomForRisk = getRandomBetween(1, 100);
// Если сгенерированное число меньше чем указанный процент вероятности,
// считаем что риск случился
if (r.probability > randomForRisk) {
// Выбираем случайное количество задач между min и max указанное в риске
let countTasks = getRandomBetween(r.min, r.max);
// Добавялем их к проекту
prjTasks += countTasks;
}
});
let IndexIteration = 0;
while (prjTasks > 0) {
// Выбираем случайное число из набора Throughput
let randomIndex = Math.floor(Math.random() * len);
// Получаем какое количество задач нужно отнять в эту неделю
let countTasks = historicalDataThroughput[randomIndex];
// Отнимаем это количество задач
prjTasks = prjTasks - countTasks;
// Переходим к следующей итерации
IndexIteration += 1;
}
// Фиксируем какая итерация получилась
let i = result.get(IndexIteration);
result.set(IndexIteration, i ? i + 1 : 1);
}
let sum = 0;
// Готовим данные к показу результата
return Array.from(result, ([name, value]) => ({ iteration: name, count: value }))
.sort((a, b) => a.iteration - b.iteration)
.map((i) => {
sum += i.count;
return {
...i,
probability: sum / numberOfExperiments
}
})
}
function getRandomBetween(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment