При решении практических задач с завидной регулярностью приходится сталкиваться с двумя типовыми подходами, которые выдвигаются на совещаниях или витают в воздухе.
- Вариант 1 — давайте решать частную задачу в общем виде. Решив ее таким образом, мы сможем попутно много чего еще порешать.
- Вариант 2 — давайте для предсказания поведения нелинейной системы притянем сюда прогнозы, ML и массу модных штучек. Круто ведь.
Однако не всегда применение таких подходов адекватно исходной постановке задачи.
Является продолжением предыдущих публикаций.
Почему такие подходы могут быть избыточными? Да по многим причинам: время поиска решения задачи, время расчетов, требования к объему вычислительных ресурсов, проведение расчетов с завышенной точностью, построение неправильной модели, высокая сложность аналитического решения прямой задачи, высокая сложность решения обратной задачи и многое другое.
Но есть классический способ решения подобных задач, который особенно хорош при наличии под руками мощного вычислителя. Метод Монте-Карло. Стат. анализ результатов многократного решения прямой задачи, которая, как правило, хорошо алгоритмизируема.
Нужные ответы, в первом приближении, можно получить за несколько часов, включая понимание задачи, кодинг и проведение прикидочных расчетов.
Ниже просто 2 примера.
Пример 1. Комбинаторика вложенных списков
Исходная бизнес-задача состоит в расширении обучающей базы чат-бота на основе малого набора входных фраз. Большой выборки исходных фраз просто физически нет. Но ее можно существенно расширить путем предварительного анализа структуры предложений и генерации перестановок, разрешенных правилами языка. Например, "труба течет в помещении 6" и "в помещении 6 течет труба" идентичны по сути.
Итак, формализованная постановка. Есть многоуровневый список вложенных списков. Необходимо сгенерировать почти все возможные перестановки исходного списка значений при условии допустимости перестановок элементов только в рамках каждого отдельного списка.
ll <- list(list('a', 'b', 'c'), 'd', list('e', 'f', list('g', 'h', 'i')))
# общая комбинаторика
factorial(3) * factorial(3) * factorial(3) * factorial(3)
# в цикле развернем всю перестановку единичного списка
ff <- function(x){
# если на входе список, то мы делаем для него перестановку и загоняем в рекурсию
res <- if(is.list(x)) {
sample(x, length(x), replace = FALSE) %>%
purrr::map(ff)
} else {
x
}
res
}
procLine <- function(row){
purrr::map(row, ff) %>%
# превращаем в вектор
rlang::squash_chr() %>%
stri_c(collapse = "")
}
# создаем список прецедентов, экспериментируем с размером выборки
wks <- 1
future::plan(multiprocess, workers = wks)
tic(glue("Generating permutations @ {wks} thread(s)"))
df1 <- purrr::map(1:10^4, ~sample(ll, length(ll), replace = FALSE)) %>%
# для каждого прецедента делаем рекурсивный процессинг до плоского вида
# purrr::map_chr(procLine) %>%
furrr::future_map_chr(procLine) %>%
enframe(name = NULL) %>%
distinct()
toc()
Пример 2. Прогнозирование очереди клиентов
Схема измерений такова, что можно снимать внешние показатели процесса (считаем его достаточно сложным и нестационарным по времени), такие как время прихода и ухода покупателей, структура корзины и многое другое.
Вариант №1 — решение задачи в лоб. Нулевые теоретические знания исполнителя + накопление исторической массы всевозможных внешних показателей (фич) и применение ML методов для "подгонки". Какие-то прогнозы строятся, но что, почему и как — остается за рамками, надо использовать лианеризованных "толмачей".
Вариант №2 — Используем научный способ познания. Открываем теорию систем массового обслуживания, строим стат. показатели процессов по параметрам СМО, запускаем параметризованную дискретную симуляцию обслуживания клиентов. Получаем "цифровую модель" Системы, обладающую рычажками для управления и аналитической объясняющей силой, основанной именно на природе наблюдаемых объектов, а не на наборе неких "фич".
В R есть для этого отличный пакет simmer
. Вся информация и масса практических примеров по ссылке.
Предыдущая публикация — «Несколько штрихов о работе с идентификаторами bigint в R».