我尝试在ProjectEuler上解决问题443,https://projecteuler.net/problem=443。
为了找到g的模式,我只是在Haskell中实现了g
g::Int->Int
g 4=13
g n=g (n-1)+n `gcd` g(n-1)
但计算列表t=[g i|i<-[4..100]]
需要的时间超过1秒。
为什么会这样?即使没有记忆,每g(n)只需要O(n)。
编辑:当我在ideone中尝试此代码时,它很好,但在我的计算机中它不是。这是haskell版本的问题吗?我在Windows上使用版本ghc 7.8.3,而使用ghc 7.6。
答案 0 :(得分:7)
每个g(n)只需要O(n)
不完全是。在你的递归案例中
g n=g (n-1)+n `gcd` g(n-1)
您正在呼叫g
两次。这意味着您实际上正在获得指数运行时间(O(2n)
)。
要确保g (n-1)
在每个步骤中仅评估一次,请使用let
或where
语句,以便您可以引用单个值 - 一次调用的结果 - 两次。
g :: Int -> Int
g 4 = 13
g n = let r = g (n-1)
in r + n `gcd` r
当我在ideone中尝试使用此代码时,它很好,但在我的计算机中却没有。这是haskell版本的问题吗?
可能,虽然是一个优化级别参数问题。 Haskell的编译器可以在检测到表达式(如g (n-1)
)出现两次时优化诸如你的函数,但搜索这些函数代价高昂且需要启用。
答案 1 :(得分:5)
如果您将g
的定义更改为:
g::Int->Int
g 4=13
g n=x+n `gcd` x
where x = g (n-1)
然后,即使在ghci
或runhaskell
下,我也可以接受运行时间(而不是ghc -O2
。)