【问题标题】:Avoiding Stack overflow with Ackermann function in R在 R 中使用 Ackermann 函数避免堆栈溢出
【发布时间】:2019-04-05 16:00:52
【问题描述】:

我最近看到一个关于 Ackermann 函数的有趣 Computerphile Video 并尝试在 R 中重新创建它,这就是我想出的:

Ackermann <- function(m,n){

  if (m == 0){

    return(n+1)

  } else if (m > 0 & n == 0){

    return(Ackermann(m-1,1))

  } else if (m > 0 & n > 0){

    return(Ackermann(m-1,Ackermann(m,n-1)))

  }

}

在视频中,他们实现了自己的代码版本(我认为是 C 语言),并解释说对于特定值对(例如 4,1)需要大量递归计算,并且需要 3 分钟来计算那个值。如果我尝试用我的算法在 R 中重新创建它,我会得到堆栈溢出:

Error: C stack usage  7971652 is too close to the limit

有没有办法在 R 中获得 Ackermann(4,1) 的结果?

【问题讨论】:

  • R 没有以有效的方式进行尾递归(我认识到这不是真正的尾递归),因此它可以(如您所见)填满堆栈。有一些技术可以解决它,例如trampolines。当它不是真正的尾递归时(如在这个函数中),它们是有限的并且不能(如果我没看错的话)正确处理它。
  • stackoverflow.com/q/13208963/3358272 的可能重复项(由于其最终依赖于递归)。

标签: r recursion stack-overflow ackermann


【解决方案1】:

我认为这是可能的,但可能相当复杂。如果你这样写(见下文),它不会出错,但需要相当长的时间:

sub_Ackermann1 <- function(df){
  i <- nrow(df)
  m <- df$m[i]
  n <- df$n[i]
  if (m == 0){
    r <- n+1
    df$r[i] <- r
    df_i <- df} 
  else if (m > 0 & n == 0){
    r <- NA
    m <- m-1
    n <- 1
    df_i <- df
    newrow <- data.frame(m=m,n=n,r=r)
    df_i <- rbind(df_i,newrow)} 
  else if (m > 0 & n > 0){
    r1 <- NA
    m1 <- m-1
    n1 <- NA
    df_i <- df
    newrow1 <- data.frame(m=m1,n=n1,r=r1)
    df_i <- rbind(df_i,newrow1)

    r2 <- NA
    m2 <- m
    n2 <- n-1
    newrow2 <- data.frame(m=m2,n=n2,r=r2)
    df_i <- rbind(df_i,newrow2)}

  return(df_i)
}

sub_Ackermann2 <- function(df){
  r <- df$r[nrow(df)]
  if (is.na(df$n[nrow(df)-1])){ 
    df$n[nrow(df)-1] <- r }
  else if (is.na(df$r[nrow(df)-1])){ df$r[nrow(df)-1] <- r}
  df_i <- df[-nrow(df),] 
  return(df_i)
}
Ackermann <- function(m,n){
  df <- data.frame(m=m,n=n,r=NA)
  if (m == 0){df$r <- n+1} 
  while (is.na(df$r[1])){
    if (is.na(df$r[nrow(df)])){ df <- sub_Ackermann1(df)}
    else if (is.na(df$r[1])){ df <- sub_Ackermann2(df)}
  }
  return(df$r[1])

}

它至少适用于较小的值,并且不会在较大的值上崩溃。也许有人可以证明这是行不通的,或者反之亦然,有想法如何优化它......

【讨论】:

  • 不错!多谢!你能解释一下为什么将对象保存在数据框中可以避免堆栈溢出吗?
  • 好吧,不是将需要处理的请求存储在调用堆栈中,而是将其存储在 RAM 中的数据帧中,因此从技术上讲,RAM 现在是限制因素。另一种选择是将相同的信息写入文件 (csv/txt) 到硬盘。
猜你喜欢
  • 2022-07-17
  • 2016-07-12
  • 2020-03-08
  • 1970-01-01
  • 2015-11-14
  • 2010-11-30
  • 1970-01-01
  • 2013-06-28
  • 2013-10-29
相关资源
最近更新 更多