问题描述
我正在尝试解决问题#14 of Project Euler。 因此,主要目的是找到Collatz序列的长度。
首先,我用常规循环解决了问题:
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
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
语句,并在调用堆栈中添加了一层。