【发布时间】:2012-08-15 00:51:30
【问题描述】:
我目前正在尝试优化我在 Projet Euler 的problem 14 解决方案。 我真的很喜欢 Haskell,我认为它非常适合解决这类问题,这是我尝试过的三种不同的解决方案:
import Data.List (unfoldr, maximumBy)
import Data.Maybe (fromJust, isNothing)
import Data.Ord (comparing)
import Control.Parallel
next :: Integer -> Maybe (Integer)
next 1 = Nothing
next n
| even n = Just (div n 2)
| odd n = Just (3 * n + 1)
get_sequence :: Integer -> [Integer]
get_sequence n = n : unfoldr (pack . next) n
where pack n = if isNothing n then Nothing else Just (fromJust n, fromJust n)
get_sequence_length :: Integer -> Integer
get_sequence_length n
| isNothing (next n) = 1
| otherwise = 1 + (get_sequence_length $ fromJust (next n))
-- 8 seconds
main1 = print $ maximumBy (comparing length) $ map get_sequence [1..1000000]
-- 5 seconds
main2 = print $ maximum $ map (\n -> (get_sequence_length n, n)) [1..1000000]
-- Never finishes
main3 = print solution
where
s1 = maximumBy (comparing length) $ map get_sequence [1..500000]
s2 = maximumBy (comparing length) $ map get_sequence [500001..10000000]
solution = (s1 `par` s2) `pseq` max s1 s2
现在,如果您查看实际问题,则存在很大的缓存潜力,因为大多数新序列将包含之前已经计算过的子序列。
为了比较,我也用 C 写了一个版本:
缓存运行时间:0.03 秒
无缓存运行时间:0.3 秒
这简直是疯了!当然,缓存将时间减少了 10 倍,但即使没有缓存,它仍然比我的 Haskell 代码快至少 17 倍。
我的代码有什么问题? 为什么 Haskell 不为我缓存函数调用?由于函数是纯缓存,缓存不应该是微不足道的,只是可用内存的问题?
我的第三个并行版本有什么问题?为什么没有完成?
将 Haskell 视为一种语言,编译器是否会自动并行化某些代码(折叠、映射等),还是必须始终使用 Control.Parallel 显式完成?
编辑:我偶然发现了this 类似的问题。他们提到他的函数不是尾递归的。我的 get_sequence_length 是尾递归的吗?如果不是,我该怎么做?
Edit2:
致丹尼尔:
非常感谢回复,真的很棒。
我一直在玩弄你的改进,但发现了一些非常糟糕的问题。
我在 Windws 7(64 位)、3.3 GHZ 四核和 8GB RAM 上运行测试。
正如你所说,我做的第一件事是用 Int 替换所有 Integer,但是每当我运行任何主电源时,我的内存都用完了,
即使 +RTS kSize -RTS 设置得高得离谱。
最终我找到了this(stackoverflow 很棒...),这意味着由于 Windows 上的所有 Haskell 程序都以 32 位运行,因此 Int 溢出导致无限递归,哇...
我改为在 Linux 虚拟机(使用 64 位 ghc)中运行测试,得到了类似的结果。
【问题讨论】:
-
main3中有一个额外的零...
标签: haskell caching optimization functional-programming