我目前正在試圖在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內存上運行測試。
我所做的第一件事就像你說的用INT替換所有Integer,但是每當我運行任何主電源時,我用盡內存, 即使+ RTS kSize -RTS設置得過高。
最後我終於找到this(計算器是真棒......),這意味着由於在Windows上的所有Haskell程序作爲32位運行時,INTS被溢出導致無限遞歸,只是哇...
我在Linux虛擬機(使用64位ghc)中運行測試,結果相似。
你有'main3' ... – 2012-08-15 01:55:51