218 lines
6.1 KiB
Python
218 lines
6.1 KiB
Python
"""随机模拟:Monte Carlo / 马尔可夫 / 随机游走 / 排队。
|
||||
|
|
|
|||
|
|
★ 本模块的 `monte_carlo` 与 `monte_carlo_integral` 是 S 档,建议自己实现。
|
|||
|
|
原建议里的 `brownian_motion` `geometric_brownian_motion` 已删(niche),
|
|||
|
|
`simulate_queue` 也已删(等于自己写一个离散事件仿真引擎,成本远高于收益)。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from collections.abc import Callable
|
|||
|
|
from typing import Any
|
|||
|
|
|
|||
|
|
__all__ = [
|
|||
|
|
"markov_simulate",
|
|||
|
|
"markov_steady_state",
|
|||
|
|
"mm1_metrics",
|
|||
|
|
"monte_carlo",
|
|||
|
|
"monte_carlo_integral",
|
|||
|
|
"monte_carlo_probability",
|
|||
|
|
"random_walk",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def monte_carlo(
|
|||
|
|
sampler: Callable[..., Any],
|
|||
|
|
simulator: Callable[..., Any],
|
|||
|
|
n: int = 10000,
|
|||
|
|
random_state: int | None = None,
|
|||
|
|
**kwargs: Any,
|
|||
|
|
) -> Any:
|
|||
|
|
"""通用蒙特卡洛:采样 → 仿真 → 汇总。
|
|||
|
|
|
|||
|
|
Parameters
|
|||
|
|
----------
|
|||
|
|
sampler:
|
|||
|
|
`f(rng, **kwargs) -> 一组参数`,例如从先验分布里抽参数。
|
|||
|
|
simulator:
|
|||
|
|
`f(params) -> float`,例如"给定参数下的年收益"。
|
|||
|
|
n:
|
|||
|
|
模拟次数。**必须向量化**:`simulator` 内部不要写 Python 循环,
|
|||
|
|
否则 10^4 次能跑几分钟,10^6 次直接放弃。
|
|||
|
|
|
|||
|
|
Returns
|
|||
|
|
-------
|
|||
|
|
`Result`:`data` 是 `n` 个结果组成的数组,
|
|||
|
|
`metrics={"mean", "std", "median", "p05", "p95", "ci_low", "ci_high"}`。
|
|||
|
|
|
|||
|
|
Notes
|
|||
|
|
-----
|
|||
|
|
比赛里 Monte Carlo 的收敛速度是 `O(1/√n)`——
|
|||
|
|
想少一半误差要 4 倍样本。所以论文里要给"区间随 n 收敛"的图,
|
|||
|
|
证明 `n` 已经够大(这也是 `viz.plot_convergence` 的典型用法)。
|
|||
|
|
|
|||
|
|
Examples
|
|||
|
|
--------
|
|||
|
|
>>> import numpy as np
|
|||
|
|
>>> r = monte_carlo(lambda rng, **k: rng.normal(0, 1), np.mean, n=1000, random_state=0)
|
|||
|
|
"""
|
|||
|
|
# TODO(L1) ★ S 档
|
|||
|
|
raise NotImplementedError
|
|||
|
|
|
|||
|
|
|
|||
|
|
def monte_carlo_probability(
|
|||
|
|
event: Callable[..., bool],
|
|||
|
|
sampler: Callable[..., Any],
|
|||
|
|
n: int = 10000,
|
|||
|
|
confidence: float = 0.95,
|
|||
|
|
random_state: int | None = None,
|
|||
|
|
) -> Any:
|
|||
|
|
"""估计事件发生概率,并给出区间。
|
|||
|
|
|
|||
|
|
Returns
|
|||
|
|
-------
|
|||
|
|
`Result`:`metrics={"probability", "ci_low", "ci_high", "std_error"}`。
|
|||
|
|
|
|||
|
|
Notes
|
|||
|
|
-----
|
|||
|
|
概率估计的相对误差 ≈ `1/√(n·p)`。**小概率事件(p < 1e-3)用朴素 MC 是不可行的**,
|
|||
|
|
需要重要性采样或方差缩减——遇到这类题要提前意识到,别硬跑。
|
|||
|
|
|
|||
|
|
Examples
|
|||
|
|
--------
|
|||
|
|
>>> import numpy as np
|
|||
|
|
>>> r = monte_carlo_probability(lambda v: v > 0, lambda rng, **k: rng.normal(0, 1), n=2000, random_state=0)
|
|||
|
|
>>> abs(r.metrics["probability"] - 0.5) < 0.1
|
|||
|
|
True
|
|||
|
|
"""
|
|||
|
|
# TODO(L1) ★ S 档
|
|||
|
|
raise NotImplementedError
|
|||
|
|
|
|||
|
|
|
|||
|
|
def monte_carlo_integral(
|
|||
|
|
func: Callable[..., Any],
|
|||
|
|
bounds: Any,
|
|||
|
|
n: int = 10000,
|
|||
|
|
random_state: int | None = None,
|
|||
|
|
) -> Any:
|
|||
|
|
"""用蒙特卡洛求多重积分。
|
|||
|
|
|
|||
|
|
Parameters
|
|||
|
|
----------
|
|||
|
|
bounds:
|
|||
|
|
`[(lo, hi), ...]`,维度即积分重数。
|
|||
|
|
|
|||
|
|
Notes
|
|||
|
|
-----
|
|||
|
|
高维积分是 MC 相对网格法的**唯一绝对优势**:网格法误差 `O(h^{-d})` 随维度爆炸,
|
|||
|
|
MC 始终是 `O(1/√n)`。三维以上优先考虑 MC。
|
|||
|
|
|
|||
|
|
Examples
|
|||
|
|
--------
|
|||
|
|
>>> import numpy as np
|
|||
|
|
>>> r = monte_carlo_integral(lambda X: X[:, 0] ** 2, bounds=[(0, 1)], n=200000, random_state=0)
|
|||
|
|
>>> abs(r.data - 1 / 3) < 0.01
|
|||
|
|
True
|
|||
|
|
"""
|
|||
|
|
# TODO(L1) ★ S 档:配完整函数卡片
|
|||
|
|
raise NotImplementedError
|
|||
|
|
|
|||
|
|
|
|||
|
|
def random_walk(
|
|||
|
|
n_steps: int,
|
|||
|
|
n_paths: int = 1,
|
|||
|
|
step_sampler: Callable[..., Any] | None = None,
|
|||
|
|
start: float = 0.0,
|
|||
|
|
random_state: int | None = None,
|
|||
|
|
) -> Any:
|
|||
|
|
"""随机游走轨迹。
|
|||
|
|
|
|||
|
|
Parameters
|
|||
|
|
----------
|
|||
|
|
step_sampler:
|
|||
|
|
`f(rng, size) -> 增量`,默认为 ±1 等概率(对称随机游走)。
|
|||
|
|
|
|||
|
|
Returns
|
|||
|
|
-------
|
|||
|
|
`Result`:`data` 形状 `(n_paths, n_steps + 1)`。
|
|||
|
|
|
|||
|
|
Examples
|
|||
|
|
--------
|
|||
|
|
>>> r = random_walk(n_steps=100, n_paths=5, random_state=0)
|
|||
|
|
>>> r.data.shape
|
|||
|
|
(5, 101)
|
|||
|
|
"""
|
|||
|
|
# TODO(L1)
|
|||
|
|
raise NotImplementedError
|
|||
|
|
|
|||
|
|
|
|||
|
|
def markov_simulate(
|
|||
|
|
transition: Any,
|
|||
|
|
initial: Any,
|
|||
|
|
steps: int = 100,
|
|||
|
|
n_paths: int = 1,
|
|||
|
|
random_state: int | None = None,
|
|||
|
|
) -> Any:
|
|||
|
|
"""马尔可夫链模拟,返回状态序列。"""
|
|||
|
|
# TODO(L1)
|
|||
|
|
raise NotImplementedError
|
|||
|
|
|
|||
|
|
|
|||
|
|
def markov_steady_state(transition: Any, max_iter: int = 10000, tol: float = 1e-10) -> Any:
|
|||
|
|
"""平稳分布。
|
|||
|
|
|
|||
|
|
Parameters
|
|||
|
|
----------
|
|||
|
|
transition:
|
|||
|
|
行随机矩阵(每行和为 1)。
|
|||
|
|
|
|||
|
|
Returns
|
|||
|
|
-------
|
|||
|
|
`Result`:`data` 是平稳分布向量,`metrics={"converged", "iterations"}`。
|
|||
|
|
|
|||
|
|
Notes
|
|||
|
|
-----
|
|||
|
|
用迭代法而不是特征分解:迭代法能顺便告诉你"收敛快不快",
|
|||
|
|
而收敛速度本身就是论文里值得写一句的性质。
|
|||
|
|
**不可约 + 非周期**才有唯一平稳分布,周期链会不收敛,要检查。
|
|||
|
|
|
|||
|
|
Examples
|
|||
|
|
--------
|
|||
|
|
>>> import numpy as np
|
|||
|
|
>>> P = np.array([[0.9, 0.1], [0.2, 0.8]])
|
|||
|
|
>>> r = markov_steady_state(P)
|
|||
|
|
>>> bool(np.allclose(r.data, [2 / 3, 1 / 3], atol=1e-6))
|
|||
|
|
True
|
|||
|
|
"""
|
|||
|
|
# TODO(L1)
|
|||
|
|
raise NotImplementedError
|
|||
|
|
|
|||
|
|
|
|||
|
|
def mm1_metrics(arrival_rate: float, service_rate: float) -> Any:
|
|||
|
|
"""M/M/1 排队系统的闭式指标。
|
|||
|
|
|
|||
|
|
Parameters
|
|||
|
|
----------
|
|||
|
|
arrival_rate, service_rate:
|
|||
|
|
到达率 λ 与服务率 μ。`λ >= μ` 时系统不稳定,队列长度无界——
|
|||
|
|
这种情况下函数返回 `success=False` 并说明原因,而不是返回 `inf`。
|
|||
|
|
|
|||
|
|
Returns
|
|||
|
|
-------
|
|||
|
|
`Result`:`metrics={"rho", "L", "Lq", "W", "Wq"}`
|
|||
|
|
(系统利用率、系统内平均顾客数、队列长度、平均逗留时间、平均等待时间)。
|
|||
|
|
|
|||
|
|
Notes
|
|||
|
|
-----
|
|||
|
|
M/M/1 是唯一值得手写的排队模型:公式是闭式的,10 行搞定。
|
|||
|
|
多服务员(M/M/c)和一般服务时间分布(M/G/1)在美赛出现频率极低,
|
|||
|
|
真遇到时现场用仿真做,不做通用封装。
|
|||
|
|
|
|||
|
|
Examples
|
|||
|
|
--------
|
|||
|
|
>>> r = mm1_metrics(arrival_rate=2, service_rate=3)
|
|||
|
|
>>> round(r.metrics["rho"], 4)
|
|||
|
|
0.6667
|
|||
|
|
"""
|
|||
|
|
# TODO(L1)
|
|||
|
|
raise NotImplementedError
|