You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Monte Carlo复现中重置simmer环境并生成新timeout随机值的方法

问题:Simmer蒙特卡洛复现中复用环境时随机timeout值重复的问题

我希望对simmer模拟进行Monte Carlo复现,想了解是否可以不在复现循环中调用simmer::simmer(),同时确保每次迭代都能生成新的simmer::timeout()随机值。

我的大致设置如下:

env <- setup_sim(input_params)
results <- run_sim(env, n_mc = 100)

其中setup_sim()是初始化simmer环境并添加资源和轨迹的函数,我希望将环境创建与模拟执行分离,因为通过编程定义环境的耗时并不短。

随后我调用run_sim()执行模拟,每次重置simmer环境:

run_sim <- function(.env, run_until = Inf, n_mc = 1, use_parallel = FALSE) {
  # Run simulation
  start_time <- Sys.time()

  print('Simulating...')
  if (use_parallel == TRUE & n_mc > 1) {
    envs <- parallel::mclapply(1:n_mc, function(i) {
      .env |>
        simmer::reset() |>
        simmer::run(until = run_until) |>
        simmer::wrap()
      })
    } else {
      envs <- purrr::map(
        seq.int(1, n_mc),
        function(i) {
          .env |>
            simmer::reset() |>
            simmer::run(
              until = run_until,
              progress = simmer_progress
              )
        },
        .progress = pb_bar_options
        )
      }

  duration <- Sys.time() - start_time
  print('Simulation complete. ')
  print(duration)

  envs
}

但我注意到,即使timeout的随机数是通过函数调用生成的,其值在各次复现中并未改变:

... |>
    simmer::timeout(
        \() {
          step_nbr <- simmer::get_attribute(env, keys = 'step_nbr')
          cycle_time <- get_cycle_time_wrapper(
            step_nbr,
            simmer::get_selected(env),
            rand_seed = sample.int(100000, 1)
            ) *
            simmer::get_attribute(env, keys = 'TimeMultiplier')

          cycle_time
        }
        ) |>
...

其中get_cycle_time_wrapper()是从查找表中随机抽样的函数。

请问在当前设置下,是否有办法确保每次复现都能生成新的随机样本,还是必须重新创建simmer环境?


解决方案

核心问题原因

你遇到的问题根源有两个:

  • 默认情况下simmer::reset()不会重置simmer环境内置的随机数生成器(RNG)状态,导致每次运行时随机序列会延续上一次的结果;
  • 你在timeout的匿名函数中使用了全局R的sample.int(),而非simmer专属的随机函数,这会脱离simmer的RNG管理,容易出现重复随机值的情况。

具体解决步骤

  1. 重置simmer环境时同时重置RNG
    在调用reset()时添加reset_rng = TRUE参数,确保每次蒙特卡洛迭代都会重置simmer的随机数状态:

    # 串行循环中修改
    .env |>
      simmer::reset(reset_rng = TRUE) |>
      simmer::run(until = run_until, progress = simmer_progress)
    
    # 并行循环中同样修改
    .env |>
      simmer::reset(reset_rng = TRUE) |>
      simmer::run(until = run_until) |>
      simmer::wrap()
    
  2. 改用simmer内置的随机函数
    替换你代码中全局的sample.int()等随机函数,使用simmer提供的simmer::sample()(或其他simmer专属随机函数,如simmer::rnorm()、simmer::runif()),这些函数会绑定当前simmer环境的RNG,确保每次运行都能生成新的随机值。
    调整你的timeout代码:

    ... |>
      simmer::timeout(
        \() {
          step_nbr <- simmer::get_attribute(env, keys = 'step_nbr')
          # 使用simmer::sample替代全局sample.int
          rand_seed <- simmer::sample(1:100000, size = 1)
          cycle_time <- get_cycle_time_wrapper(
            step_nbr,
            simmer::get_selected(env),
            rand_seed = rand_seed
          ) * simmer::get_attribute(env, keys = 'TimeMultiplier')
    
          cycle_time
        }
      ) |>
    ...
    

    如果get_cycle_time_wrapper()内部也有随机抽样逻辑,同样建议替换为simmer的随机函数,确保全程使用simmer的RNG管理。

  3. 并行场景下的额外注意
    使用mclapply时,每个子进程会复制原始环境的状态,包括RNG状态。虽然已经设置了reset_rng = TRUE,但为了保险,可以在每个并行任务中显式设置不同的初始种子:

    envs <- parallel::mclapply(1:n_mc, function(i) {
      .env |>
        simmer::reset(reset_rng = TRUE) |>
        # 手动设置不同的种子,增强随机性
        simmer::set_seed(i * 100) |>
        simmer::run(until = run_until) |>
        simmer::wrap()
    })
    

是否需要重新创建环境?

不需要重新创建simmer环境!通过上述调整,你可以复用同一个初始化好的环境,每次迭代仅重置环境状态和RNG,就能得到不同的随机timeout值,完全满足蒙特卡洛复现的需求,同时保留环境初始化的性能优势。


内容的提问来源于stack exchange,提问作者Aorus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:55:05