向量的阶乘

时间:2019-02-24 11:08:50

标签: r factorial

作为一个新手,我试图定义自己的函数来计算阶乘。我设法构造了最适合数字的函数。

fact1 = function(x){
    a=1 
    for(i in 1:x){
        a = a*i
    }
    return(a)
}   

factorial = function(x){
    ifelse(x>=0 & round(x) == x , fact1(as.integer(x)),"NA")
}

但是,我该如何改进它才能将向量输入其中并计算每个元素的阶乘?

5 个答案:

答案 0 :(得分:2)

除了上面的b注释之外,您还可以使用b = List.copyOf(a); System.out.println("A start:" + b.get(2).getSeating()); System.out.println("B start:" + b.get(2).getSeating()); a.get(2).setSeating(27); System.out.println("Hi there" ); System.out.println("A end:" + a.get(2).getSeating()); System.out.println("B end:" + b.get(2).getSeating()); lapply返回向量而不是列表:

vapply

答案 1 :(得分:2)

对于Vectorize来说,这似乎是一个完美的例子:只需在Vectorize函数的定义周围使用factorial,使其在输入上进行矢量化即可。

fact1 = function(x){
  a=1 
  for(i in 1:x){
    a = a*i
  }
  return(a)
}   

factorial = Vectorize(function(x){
  ifelse(x>=0 & round(x) == x , fact1(as.integer(x)),"NA")
})

factorial(c(1,2,3))
#> [1] 1 2 6

答案 2 :(得分:2)

问题答案似乎有些复杂。 阶乘已经是一个存在的函数,可以向量化这样,如果您有一些数据,则可以将其简单地放入函数中。如果要定义负数以返回0,也可以使用逻辑语句将其合并。请注意,我正在使用下面的内置函数factorial,而不是问题中的那个。

dat <- round(runif(1000, -10, 10))
dat_over_zero <- dat > 0 
fact_vector <- numeric(1000)
fact_vector <- factorial(dat[dat_over_zero])

现在,如果您只是创建一个要学习的练习,则可以使用相同的思想非常简单地将函数向量化,避免不必要的循环。只需使用一个循环,并在此循环中迭代向量中的每个元素。

R_factorial <- function(x){
  if(!is.numeric(x) || length(dim(x)))
    stop("X must be a numeric vector!")
  #create an output vector
  output <- numeric(NROW(x))
  #set initial value
  output[x >= 1] <- 1
  output[x < 1] <- NA
  #Find the max factor (using only integer values, not gamma approximations)
  mx <- max(round(x))
  #Increment each output by multiplying the next factor (only on those which needs to be incremented) 
  for(i in seq(2, mx)){
    output[x >= i] <- output[x >= i] * i
  }
  #return output
  output
}

一些注意事项:

  1. 首先使用output <- numeric(length)分配整个矢量,其中length是输出的数量(例如,此处是length(x)或更普遍地是NROW(x))。
  2. 使用R常量NA代替"NA"而不使用任何数值。第一个被识别为数字,而第二个将更改字符向量中的向量。

现在,替代答案表明是lapply还是vapply。这与遍历向量中的每个值并在每个值上使用函数大致相同。因此,对函数进行矢量化通常是一种缓慢的方法(但很容易理解!)。如果可以避免这种情况,则通常可以提高速度。 For循环和apply不一定是坏事,但与矢量化函数相比,它通常要慢得多。参见this stackoverflow page,它以一种非常容易理解的方式解释了原因。 另一个替代方法是使用建议的Vectorize函数。这是一个肮脏的解决方案。以我的经验,它通常比执行一个简单的循环要慢,并且它可能会对多个参数函数产生一些意想不到的副作用。它不一定很糟糕,因为人们经常会获得底层代码的可读性。


速度比较

现在,矢量版本比其他答案要快得多。使用microbenchmark包中的microbenchmark函数,我们可以确切地看到速度有多快。下面显示了多少(请注意,我在问题描述中使用阶乘函数):

microbenchmark::microbenchmark(R_factorial = R_factorial(x),
                               Vapply = vapply(x,
                                              factorial, 
                                              FUN.VALUE = numeric(1)),
                               Lapply = lapply(x, factorial),
                               Vfactorial = Vfactorial(x))
Unit: microseconds
        expr       min        lq      mean    median       uq       max neval
 R_factorial   186.525   197.287  232.2394  212.9565  241.464   395.706   100
      Vapply  2209.982  2354.596 3004.9264 2428.7905 3842.265  6165.144   100
      Lapply  2182.041  2299.092 2584.3881 2374.9855 2430.867  5061.852   100
Vfactorial(x) 2381.027 2505.4395 2842.9820 2595.3040 2669.310  5920.094   100

正如人们所看到的,R_factorial比vapply或lapply快11-12倍(2428.8 / 212.96 = 11.4)。这是一个巨大的速度提升。可以做进一步的改进以进一步提高速度(例如使用阶乘近似算法,Rcpp和其他选项),但对于本示例来说,就足够了。

答案 3 :(得分:0)

使用lapply函数

lapply(c(1,2,3),factorial)
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 6

R Documentation for lapply function

答案 4 :(得分:0)

您还可以使用类型safe purrr :: map_dbl-function:

purrr::map_dbl(c(1,2,3), fact1)

[1] 1 2 6