【问题标题】:R Simulation ProgrammingR 仿真编程
【发布时间】:2018-09-04 17:39:47
【问题描述】:

赌徒废墟。在这种情况下,赌徒以 6 美元开始。想象一下游戏是抛硬币,它有 1/2 的赢/输概率。现在,每场胜利给你 1 美元,每场失败 -1 美元。下面的代码将多次模拟这种情况,如果他达到 0$ 或某个金额,例如 10$,它将停止。但问题是我不知道如何存储他的股份轨迹,例如在试验 1 中将显示 6 5 4 3 2 1 0。我该怎么做?

gamble <- function(k,n,p) {                                             
   stake <- k                                   
   while (stake > 0 & stake < n) {
         bet <- sample(c(-1,1),1,prob=c(1-p,p))
         stake <- stake + bet }                                                     
    if (stake == 0) return(1) else return(stake)}  
         storage <- vector("list", 100)                                     
         k <- 6       
         n <-  10  
         p <- 1/2  
         trials <- 100
    simlist <- replicate(trials, gamble(k, n, p))              
    print(simlist)

【问题讨论】:

    标签: r simulation


    【解决方案1】:

    我修改了gamble,这样stake 不是每次都更新一个stake 值,而是一个向量,我们用i 跟踪我们在其中的位置。一种可怕的方法是在每次迭代时将一个新值附加到stake - 一次使向量更长一个项目是非常低效的。相反,我们用一个慷慨的 10k NA 值初始化 stake。如果我们用完了,我们会在最后再粘上 10k。

    否则我会尽可能多地保留您的代码。

    gamble <- function(k, n, p) {
      stake <- rep(NA_real_, 1e4)
      i <- 1
      stake[1] <- k
      while (stake[i] > 0 & stake[i] < n) {
        bet <- sample(c(-1, 1), 1, prob = c(1 - p, p))
        stake[i + 1] <- stake[i] + bet
        i <- i + 1
        if (length(stake) == i) stake <- c(stake, rep(NA_real_, 1e4))
      }
      return(stake[!is.na(stake)])
    }
    
    k <- 6
    n <-  10
    p <- 1 / 2
    trials <- 100
    simlist <- replicate(trials, gamble(k, n, p))
    head(simlist)
    # [[1]]
    # [1] 6 5 4 3 4 3 2 1 0
    # 
    # [[2]]
    #  [1]  6  7  6  5  6  5  4  3  2  3  4  3  4  5  4  5  4  5  6  7  8  7  8  7  6  7  8  7
    # [29]  8  9  8  7  6  7  6  7  8  9 10
    # 
    # [[3]]
    # [1] 6 5 4 3 2 1 0
    # 
    # [[4]]
    #  [1] 6 7 8 9 8 7 6 5 6 5 4 5 6 7 6 7 6 5 4 3 2 3 2 3 4 3 2 1 2 1 0
    # 
    # [[5]]
    #  [1] 6 5 6 5 4 3 4 3 2 3 4 3 4 3 4 3 4 3 4 5 4 5 6 5 6 7 6 5 4 5 4 5 4 3 4 3 2 1 2 1 2 3
    # [43] 2 3 2 3 2 1 0
    # 
    # [[6]]
    #  [1]  6  7  6  7  8  7  6  7  8  9 10
    

    【讨论】:

    • 。谢谢你。我有一个问题:我可以限制跟踪我的赌注吗?因为我注意到有一些模拟需要跟踪很多赌注。我可以将模拟的最大值限制为 20 吗?所以即使它不会以 0 或 1 结束,如果达到 20 个赌注,它也会停止。
    • 当然可以! i 跟踪到目前为止每次模拟中的投注数量,所以我敢打赌,如果您尝试将基于 i 的条件添加到 while 循环中,您可以弄清楚。如果您知道最大可能下注数为 20,您也可以简化我的代码,您绝对不需要 10k+ 次迭代。
    • 假设我已经弄清楚了,现在我想计算以 0 或 10 结尾的模拟。有没有办法做到这一点?我知道如何计算每个列表的总数,但只识别那些以给定特定数字结尾的列表对我来说听起来很神秘。顺便说一句,您非常感谢您的帮助先生。我是 R 的新手,这就是原因。
    • sapply(simlist, tail, 1) 从每个列表中获取最后一个值。剩下的就交给你了。
    【解决方案2】:

    这是 gamble 函数的修改版本:在 while 循环之前初始化的空 track 数组将跟踪赌注的不同值,直至达到最小值或最大值

    gamble <- function(s, mi, ma, p){
      stake <- s
      track <- array()
      counter <- 1
      while(stake > mi & stake < ma) {
        bet <- sample(c(-1,1),1,prob=c(1-p,p))
        stake <- stake + bet
        track[counter] <- stake
        counter = counter + 1
        if (counter > 20) break
      }
      return(track)
    }
    
    p <- 0.5
    starting_value <- 6
    mi <- 0
    ma <- 10
    trials <- 10
    #track <- gamble(starting_value, mi, ma, p)
    simlist <- replicate(trials, gamble(starting_value, mi, ma, p)) 
    
    end_sims <- vector()
    counter <- 1
    for (i in 1:trials) {
      if (simlist[[i]][length(simlist[[i]])] == 0 | simlist[[i]][length(simlist[[i]])] == 10) {
        end_sims[counter] <- i
        counter <- counter + 1
      }
    }
    

    【讨论】:

    • 感谢@SmithM。我有 2 个答案,它们的修改都不同。这显示了 1 次模拟/运行。我只是要复制这种方法。但是对于像 1 次运行显示超过 20 个值的情况,是否有一种可能的方法可以将其限制为 20,即使它不以 0 或 1 结尾?
    • 是的@RodelG.Aldema 你可以简单地插入一个break语句。检查我的编辑
    • 是否可以计算仅以 10 或 0 结尾的模拟次数?一个月前刚学了一些R的基础知识,现在我正在尝试模拟问题,嘿嘿,请原谅我的好奇心。
    • 是的,您也可以这样做。检查我编辑的答案
    • 谢谢。试图翻译最后一部分发生的事情,我注意到 simlist[[i]] 将调用每个列表。但在后者中,我仍然很困惑它是如何只收集以 0 和 10 结尾的值。我试图找出长度 [[i]]==0,我认为长度命令只是为了知道有多少值有一个列表。我对它的工作方式印象深刻。
    【解决方案3】:

    这是一种不同的方法。第一个想法是一次做很多试验。因此,我们有

    gamble0 <-
        function(n_trials, k, n, p)
    {
        ## create n_trials simulations
        stakes <- rep(k, n_trials)
        trials <- seq_len(n_trials)
    
        repeat {
            ## bet on all trials still in play, and update
            bet <- sample(c(1, -1), length(trials), TRUE, prob=c(1-p, p))
            stakes[trials] <- stakes[trials] + bet
    
            ## only continue to follow those trials that have not terminated
            trials <- trials[(stakes[trials] > 0L) & (stakes[trials] < n)]
            if (length(trials) == 0)
                break
        }
        stakes
    }
    

    结果是一个结果向量,计算速度很快,因为我们允许 R 进行 矢量化 计算(例如,调用一次 sample() 以生成 length(trials) 结果,而不是调用它 @ 987654324@次)。

    > n <- 100000
    > system.time(answer <- gamble0(n, 6, 10, .5))
       user  system elapsed 
      0.336   0.000   0.338 
    > table(answer) / n
    answer
          0      10 
    0.39973 0.60027 
    

    要在每次模拟中累积曲目,请使用list() 来跟踪仍在播放的每个曲目和试验。一旦我们记录了所有轨道的结果,通过创建轨道和试验的单个向量(通过unlist())并使用split()重新拆分轨道,将迭代列表转换为轨道列表基于轨迹的矢量。

    gamble2 <-
        function(n_trials, k, n, p)
    {
        ## lists to hold tracks
        tracks <- trials <- list()
        ## initial conditions
        i <- 1L
        stakes <- rep(k, n_trials)
        trial <- seq_len(n_trials)
        repeat {
            ## store current tracks
            tracks[[i]] <- stakes
            trials[[i]] <- trial
            ## still more to do?
            idx <- (stakes > 0L) & (stakes < n)
            if (!any(idx))
                break
            ## update tracks that are still in play
            bet <- sample(c(1, -1), sum(idx), TRUE, c(1 - p, p))
            stakes <- tracks[[i]][idx] + bet
            trial <- trials[[i]][idx]
            ## increment step
            i <- i + 1L
        }
        ## reshape results from list-of-iterations to list-of-tracks
        tracks <- unlist(tracks, use.names = FALSE)
        trials <- unlist(trials, use.names = FALSE)
        tracks <- split(tracks, trials)
        ## report results
        list(iterations = i, tracks = tracks)
    }
    

    这是相对较快的,并且可以被操纵来调查属性,例如,

    > n_trials <- 100000
    > system.time(answer <- gamble2(n_trials, 6, 10, .5))
       user  system elapsed 
      2.172   0.000   2.172 
    > tracks0 <- unlist(answer$tracks, use.names=FALSE)
    > last <- cumsum(lengths(answer$tracks))
    > table(tracks0[last]) / n_trials
    
          0      10 
    0.39794 0.60206 
    > hist(lengths(answer$tracks))
    

    (gamble1(),自从被删除后,试图变得过于聪明,使用环境来存储迭代;R 在增长向量和列表方面变得更好,所以这种聪明是不必要的;这是也与@Gregor 的避免增长向量的建议相关——通过索引超过末尾 x[i]x[[i]] 来增长向量现在在 R 中具有合理的性能。

    【讨论】:

    • 感谢您采用这种不同的方法。现在对我来说可能看起来很复杂,但我有一些问题:为什么当我尝试运行 track0 时它会发送此错误“answer$tracks 中的错误:'closure' 类型的对象不是子集”
    • 我更新了函数gamble2(),请重试。
    • 我还在研究你的每一行代码是如何工作的。很快我就会明白这一点。谢谢你的想法
    猜你喜欢
    • 2014-09-01
    • 2013-03-19
    • 2010-10-31
    • 1970-01-01
    • 2014-12-07
    • 2014-05-26
    • 1970-01-01
    • 1970-01-01
    • 2018-09-11
    相关资源
    最近更新 更多