即使使用R进行记忆,递归也很慢

问题描述

我正在尝试解决问题#14 of Project Euler。 因此,主要目的是找到Collat​​z序列的长度。

首先,我用常规循环解决了问题:

compute <- function(n) {
    result <- 0
    max_chain <- 0
    hashmap <- 1
    for (i in 1:n) {
        chain <- 1
        number <- i
        while (number > 1) {
            if (!is.na(hashmap[number])) {
                chain <- chain + hashmap[number]
                break
            }
            if (number %% 2 == 0) {
                chain <- chain + 1
                number <- number / 2
            } else {
                chain <- chain + 2
                number <- (3 * number + 1) / 2
            }
        }
        hashmap[i] <- chain
        if (chain > max_chain) {
            max_chain <- chain
            result <- i
        }
    }
    return(result)
}

n = 1000000仅2秒。 我决定将while循环替换为递归

len_collatz_chain <- function(n,hashmap) {
    get_len <- function(n) {
        if (is.na(hashmap[n])) {
            hashmap[n] <<- ifelse(n %% 2 == 0,1 + get_len(n / 2),2 + get_len((3 * n + 1) / 2))
        }
        return(hashmap[n])
    }
    get_len(n)
    return(hashmap)
}

compute <- function(n) {
    result <- 0
    max_chain <- 0
    hashmap <- 1
    for (i in 1:n) {
        hashmap <- len_collatz_chain(i,hashmap)
        print(length(hashmap))
        if (hashmap[i] > max_chain) {
            max_chain <- hashmap[i]
            result <- i
        }
    }
    return(result)
}

此解决方案有效,但是效果很慢。 n = 10000将近1分钟。 我认为原因之一是R每次调用函数hashmap时都会创建len_collatz_chain对象。

我了解Rcpp软件包,是的,第一个解决方案可以正常工作,但我不明白我哪里写错了。 有提示吗?

例如,我的Python递归解决方案在n = 1000000

下工作1秒钟
def len_collatz_chain(n: int,hashmap: dict) -> int:
    if n not in hashmap:
        hashmap[n] = 1 + len_collatz_chain(n // 2,hashmap) if n % 2 == 0 else 2 + len_collatz_chain((3 * n + 1) // 2,hashmap)
    return hashmap[n]

def compute(n: int) -> int:
    result,max_chain,hashmap = 0,{1: 1}
    for i in range(2,n):
        chain = len_collatz_chain(i,hashmap)
        if chain > max_chain:
            result,max_chain = i,chain
    return result

解决方法

R和Python代码之间的主要区别在于,在R中,您为哈希表使用向量,而在Python中,您使用字典,并且哈希表作为函数参数被多次传输。

在Python中,如果您有一个Dictionary作为函数参数,则仅将对实际数据的引用传输到被调用函数。很快被调用函数与调用方在相同的数据上工作。

在R中,复制向量用作函数参数。这可能会很慢,但从被调用函数无法更改调用方数据的角度而言,这是更安全的。

这是Python在您的代码中速度如此之快的主要原因。

不过,您可以略微更改R代码,以使哈希图不再作为函数参数传输:

len_collatz_chain <- local({
  
  hashmap <- 1L
  
  get_len <- function(n) {
    if (is.na(hashmap[n])) {
      hashmap[n] <<- ifelse(n %% 2 == 0,1 + get_len(n / 2),2 + get_len((3 * n + 1) / 2))
    }
    hashmap[n]
  }
  
  get_len
})


compute <- function(n) {
  result <- rep(NA_integer_,n)
  
  for (i in seq_len(n)) {
    result[i] <- len_collatz_chain(i)
  }
  result
}

compute(n=10000)

这使R代码更快。 (不过,Python可能仍会更快)。

请注意,由于不需要,我还删除了R代码中的return语句,并在调用堆栈中添加了一层。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...