Files
clickstream-ch-kafka-supers…/generator/src/clickstream_generator/intensity.py
T
ddadmin 589e2321b3 feat(generator): добавлено ускорение модельного времени
- Зачем:
  - ускоренный стенд должен проходить больше модельного времени и давать соответствующий событийный бюджет.
- Что:
  - расчёт интенсивности переведён на модельную длительность тика.
  - добавлена устойчивая выборка бюджета при больших λ.
  - усилены тесты ×K, дневного коэффициента и независимости от настенного часа.
- Проверка:
  - make generator-test.
  - review gate после issue 03 пройден после исправления underflow Poisson.
2026-06-14 17:48:51 +03:00

66 lines
2.1 KiB
Python

"""Расчёт событийного бюджета тика."""
import math
import random
from datetime import datetime
from zoneinfo import ZoneInfo
from clickstream_generator.config import Config
POISSON_KNUTH_MAX_LAMBDA = 100.0
def hour_factor(now: datetime | None = None, model_timezone: str = "UTC") -> float:
"""Возвращает коэффициент интенсивности в зависимости от часа дня."""
current = now
if current is None:
raise ValueError("now is required for hour_factor")
if current.tzinfo is None:
current = current.replace(tzinfo=ZoneInfo("UTC"))
current = current.astimezone(ZoneInfo(model_timezone))
hour = current.hour
if 9 <= hour <= 18:
return 1.2
if 0 <= hour <= 5:
return 0.7
return 1.0
def _sample_poisson(lambda_tick: float, rng: random.Random) -> int:
"""Разыгрывает Poisson без underflow на больших λ."""
if lambda_tick >= POISSON_KNUTH_MAX_LAMBDA:
return max(0, round(rng.gauss(lambda_tick, math.sqrt(lambda_tick))))
count = 0
threshold = math.exp(-lambda_tick)
product = 1.0
while product > threshold:
product *= rng.random()
count += 1
return count - 1
def calculate_events_count(
config: Config,
rng: random.Random,
now: datetime | None = None,
) -> int:
"""Вычисляет количество событий для текущего тика (Poisson + jitter)."""
model_time = now or config.model_t0
factor = hour_factor(model_time, config.model_timezone)
lambda_minute = config.lambda_base_per_min * factor
model_tick_seconds = config.tick_seconds * config.model_time_speed
lambda_tick = lambda_minute * (model_tick_seconds / 60.0)
count = _sample_poisson(lambda_tick, rng)
if config.jitter_pct > 0:
jitter_factor = 1.0 + rng.uniform(
-config.jitter_pct / 100.0,
config.jitter_pct / 100.0,
)
count = int(count * jitter_factor)
return max(config.min_events_per_tick, min(count, config.max_events_per_tick))