Вот другой подход. Первая идея состоит в том, чтобы сделать много испытаний одновременно. Таким образом, вместо оригинальной реализации у нас есть
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)
результатов, а не length(trials)
* раз).
> 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).