Метод 12: calculate_stochastic_decay()

Домен: SIMULATION | Контур: Стохастическое бытовое списание продуктов в мусор

WarningОграничение публичной документации

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

  • Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).

1. Бизнес-спецификация метода

  • Идентификатор метода: BPDS-SIM-M012
  • Системное имя: calculate_stochastic_decay(raw_products: List<RawProductDto>, current_tick: Long): List<UUID>
  • Микросервис: simulation-core-engine
  • Домен: SIMULATION
  • Класс / Компонент: simulation.engine.helpers.DecayEvaluator

1.1. Описание логики работы

Этот метод выполняет роль бытового аудита холодильника, когда на Шаге 9 выпал сценарий Выброса продуктов (SCENARIO_WASTE_DISPOSAL). Поскольку у Твина в голове нет абстрактного параметра порчи (\(x_2\)), сам сценарий запускается как чисто стохастическое бытовое событие — хаотичное решение пользователя избавиться от еды.

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

Чтобы сымитировать это хаотичное поведение, метод рассчитывает «время удержания» каждого товара в часах (тактах). Чем дольше продукт лежит нетронутым, тем выше стохастическая вероятность того, что пользователь от него откажется. Метод проверяет каждый продукт случайным кубиком, собирает UUID отказных товаров и передает их списком на удаление из базы бэкенда.

1.2. Пошаговое выполнение

  1. Прием состава холодильника: Метод получает на вход актуальный список продуктов пользователя, извлеченный из приложения.
  2. Расчет времени нахождения: Для каждого продукта вычисляется дельта времени: текущий такт симуляции минус номер такта, когда продукт был куплен (added_at_tick).
  3. Определение стохастического шанса отката: На основе категории продукта (category_id) из памяти извлекается базовый часовой коэффициент отказа \(\lambda_{base}\) (например, для фруктов он выше, так как они надоедают и теряют товарный вид быстрее, чем бакалея).
  4. Калькуляция вероятности списания: Рассчитывается динамический шанс того, что продукт будет выброшен, по формуле: \(P_{waste} = 1 - e^{-\lambda_{base} \cdot \Delta t}\).
  5. Бросок кубика Бернулли: Генератор случайных чисел выдает случайную дробь от 0.0 до 1.0. Если это число меньше или равно вычисленной вероятности — продукт признается отказным, и его fridge_item_id заносится в список на утилизацию.

2. Диаграмма последовательности метода (Вход и Выход флоу)

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

sequenceDiagram
    autonumber
    participant Scenario as Сценарий Выброса Продуктов
    participant D as DecayEvaluator
    participant Math as Математический Движок

    %% ВХОД МЕТОДА
    Scenario->>D: Вызов calculateStochasticDecay(raw_products, current_tick)
    activate D
    Note over D: Вход метода: Список продуктов из холодильника бэкенда и номер текущего такта
    
    loop Построчный проход по каждому продукту в массиве
        D->>D: Вычисление времени удержания: delta_t = current_tick - added_at_tick
        
        %% МАТЕМАТИЧЕСКИЙ РАСЧЕТ СТОХАСТИЧЕСКОГО ШАНСА
        D->>Math: Расчет шанса отказа пользователя от продукта
        activate Math
        Math-->>D: Возврат: Вероятность бытового выброса P_waste
        deactivate Math
        
        Note over D: Бросок кубика: random_float = Math.random()
        
        alt Ситуация: random_float <= P_waste
            Note over D: Пользователю разонравился продукт.<br/>Добавление fridge_item_id в список мусора
        end
    end
    
    Note over D: Выход метода: Список уникальных UUID отказной еды
    D-->>Scenario: Возврат: List<UUID> (Список на удаление)
    deactivate D


3. Схемы данных и SQL-взаимодействие

Этот метод обрабатывает извлеченный массив объектов внутри оперативной памяти симулятора и напрямую запросы к PostgreSQL на этом шаге не отправляет. Все UUID продуктов уже находятся в памяти воркера.


4. Спецификация обмена данными (Вход / Выход)

Данные передаются внутри памяти микросервиса между контуром сценария списания и аналитическим модулем.

4.1. Входной массив продуктов из холодильника бэкенда (Входные параметры)

{
  "current_simulation_tick": 105,
  "raw_products_fridge": [
    {
      "fridge_item_id": "8fa1b432-8411-4c12-a111-9992345bcbc1",
      "category_id": "FRUITS",
      "sku": "BANANA_ECUADOR_KG",
      "added_at_tick": 57
    }
  ]
}

4.2. Выходной список UUID отказного товара (Выходные параметры для Метода 15)

{
  "identified_spoiled_items": [
    "8fa1b432-8411-4c12-a111-9992345bcbc1"
  ],
  "stochastic_metadata": {
    "product_retention_hours": 48,
    "calculated_p_waste": 0.8534
  }
}

5. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: BACKEND

Заголовок: Реализация калькулятора стохастического бытового списания товаров calculate_stochastic_decay

5.1. Что нужно сделать

  1. Создать Java-класс DecayEvaluator в пакете simulation.engine.helpers и пометить его аннотацией @Component.
  2. Зашить базовую матрицу интенсивностей бытового отказа от категорий Алматы во внутреннюю неизменяемую карту Map<String, Double> decayRates (FRUITS -> 0.0400, READY_MEALS -> 0.0300, DAIRY -> 0.0150, GROCERIES -> 0.0001).
  3. Написать метод calculateStochasticDecay, принимающий список DTO продуктов и номер текущего такта.
  4. Внутри цикла по продуктам вычислять время нахождения товара в холодильнике: long deltaT = currentTick - product.getAddedAtTick(). Добавить проверочный фильтр if (deltaT <= 0) continue; для предотвращения сетевых рассинхронов.
  5. Рассчитывать стохастическую вероятность бытового выброса через функцию 1.0000 - Math.exp(-lambdaBase * (double) deltaT).
  6. Бросать стохастический кубик Бернулли через ThreadLocalRandom.current().nextDouble(). Если выпавшее число меньше или равно вероятности pWaste, добавлять product.getFridgeItemId() в результирующий список List<UUID>.

6. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: МИГРАЦИЯ

Заголовок: Создание индекса idx_fridge_products_added_at в базе данных бэкенда для оптимизации выгрузки возраста продуктов

6.1. Что нужно сделать

Поскольку симулятор в сценарии выброса постоянно анализирует поле даты добавления продуктов added_at для расчета времени нахождения еды в холодильнике (\(\Delta t\)), прикладные эндпоинты бэкенда будут совершать колоссальное количество запросов к этой колонке. Чтобы снизить нагрузку на дисковую подсистему PostgreSQL бэкенда розничной сети Алматы при массовых тактах, необходимо накат B-Tree индекса на стороне бэкенда.

-- Изменения для базы данных бэкенда розничной сети (схема public)
CREATE INDEX IF NOT EXISTS idx_fridge_products_added_at_calc 
ON public.fridge_products (added_at);