R中的因子记忆化

时间:2016-12-25 17:01:11

标签: r factorial memoization

我写了这个函数来找到一个数字因子

fact <- function(n) {
    if (n < 0){
      cat ("Sorry, factorial does not exist for negative numbers", "\n")
    } else if (n == 0){
      cat ("The factorial of 0 is 1", "\n")
    } else {
    results = 1
    for (i in 1:n){
      results = results * i
    }
    cat(paste("The factorial of", n ,"is", results, "\n"))
    }
}

现在我想在R中实现Memoization。我在R上有基本思想并尝试使用它们。但我不确定这是前进的方向。你能否也请详细说明这个话题。预先感谢。 Memoized Factorial

    fact_tbl <- c(0, 1, rep(NA, 100))
    fact_mem <- function(n){
          stopifnot(n > 0)
          if(!is.na(fib_tbl[n])){
           fib_tbl[n]
    } else {
       fact_tbl[n-1] <<- fac_mem(n-1) * n
     }
   }

   print (fact_mem(4))

1 个答案:

答案 0 :(得分:7)

首先,如果您需要高效实施,请使用R的factorial功能。不要自己写。然后,阶乘是理解递归的一个很好的练习:

myfactorial <- function(n) {
  if (n == 1) return(1)
  n * myfactorial(n-1)
}

myfactorial(10)
#[1] 3628800

使用此功能只有在打算重复使用该功能时才能使用memoization。您可以使用闭包在R中实现memoization。哈德利在his book中解释了这些。

createMemFactorial <- function() {
  res <- 1
  memFactorial <- function(n) {
    if (n == 1) return(1)

    #grow res if necessary
    if (length(res) < n) res <<- `length<-`(res, n)

    #return pre-calculated value
    if (!is.na(res[n])) return(res[n])

    #calculate new values
    res[n] <<- n * factorial(n-1)
    res[n]
  }
  memFactorial
}
memFactorial <- createMemFactorial()

memFactorial(10)
#[1] 3628800

它真的更快吗?

library(microbenchmark)
microbenchmark(factorial(10),
               myfactorial(10), 
               memFactorial(10))
#Unit: nanoseconds
#             expr  min     lq    mean median     uq   max neval cld
#    factorial(10)  235  264.0  348.02  304.5  378.5  2463   100 a  
#  myfactorial(10) 4799 5279.5 6491.94 5629.0 6044.5 15955   100   c
# memFactorial(10)  950 1025.0 1344.51 1134.5 1292.0  7942   100  b 

请注意microbenchmark评估函数(默认情况下)100次。由于我们在测试memFactorial时存储了n = 10的值,因此我们只计时if条件和查找。正如您所看到的,R的实现(主要用C语言编写)更快。

更好(更简单)的示例实现斐波那契数字。这里算法本身受益于memoization。

#naive recursive implementation
fib <- function(n)  {
  if(n == 1 || n == 2) return(1)
  fib(n-1) + fib(n-2)
}

#with memoization
fibm <- function(n)  {
  if(n == 1 || n == 2) return(1)

  seq <- integer(n)
  seq[1:2] <- 1

  calc <- function(n) {
    if (seq[n] != 0) return(seq[n])
    seq[n] <<- calc(n-1) + calc(n-2)
    seq[n]
  }

  calc(n)
}

#try it:
fib(20)
#[1] 6765
fibm(20)
#[1] 6765

#Is memoization faster?
microbenchmark(fib(20),
               fibm(20))
#Unit: microseconds
#     expr      min       lq       mean    median        uq       max neval cld
# fib(20)  8005.314 8804.130 9758.75325 9301.6210 9798.8500 46867.182   100   b
#fibm(20)    38.991   44.798   54.12626   53.6725   60.4035    97.089   100  a