[英]Haskell performance : Inversion count algorithm
我決定使用Haskell從Standford算法課程https://class.coursera.org/algo-005解決第一個編程任務。 盡管我對語言很陌生,但我實現它的速度比c ++快得多。 我有6年以上的c ++工作經驗,所以給我留下了深刻的印象。 但性能令人失望:0.19秒(c ++)vs 9.88(haskell)版本。 如何提高Haskell實現的性能,使其可以與c ++相媲美?
這是我在Haskell中的代碼
data SortedList = SortedList {
inversionCount :: Int,
list :: [Int]
} deriving (Show)
-- first list accumulator
packm :: Int -> SortedList -> Int -> SortedList
packm x (SortedList count xs) add = SortedList (count + add) (x:xs)
merge2 :: [Int] -> [Int] -> SortedList
merge2 [] xs = SortedList 0 xs
merge2 xs [] = SortedList 0 xs
merge2 xlist@(x:xs) ylist@(y:ys)
| x < y = packm x (merge2 xs ylist) 0
| otherwise = packm y (merge2 xlist ys) $ length xlist
countAndMerge :: SortedList -> SortedList -> SortedList
countAndMerge (SortedList lcount lxs) (SortedList rcount rxs) =
let merged = merge2 lxs rxs
in SortedList (lcount + rcount + inversionCount merged) $ list merged
mergesort :: [Int] -> SortedList
mergesort [] = SortedList 0 []
mergesort [x] = SortedList 0 [x]
mergesort xs =
let leftsorted = mergesort $ take halfElements xs
rightsorted = mergesort $ drop halfElements xs
in countAndMerge leftsorted rightsorted
where halfElements = length xs `div` 2
main = do
contents <- getContents
let intlist = [ read x :: Int | x <- (lines contents) ]
print $ inversionCount $ mergesort intlist
最大的問題是漸近的表現是不正確的; 它是O(n ^ 2 * log n)而不是最優O(n * log n)。 罪魁禍首是merge2
:
| otherwise = packm y (merge2 xlist ys) $ length xlist
length xlist
是O(n)。 假設一個隨機輸入列表,我們需要在大約一半merge2
調用上計算length xlist
,從而使一個級別的合並O(n ^ 2)。
otherwise = packm y (merge2 xlist ys) $ length xlist
這會平均計算合並的每個其他步驟的長度。 這使得整個業務呈二次方式。
如果您不通過計算元素來跟蹤列表的長度,而是通過從頂層向下傳遞計數,則可以恢復O(N log N)行為。 對於100000個元素的列表,這意味着執行時間從20秒減少到0.45秒(在我的機器上使用-O2)。
在不改變算法的情況下進一步擴展它是有問題的,因為它目前在線性堆棧空間中運行,並且無法使用默認RTS選項處理100萬個元素。 將mergesort更改為合並 - 鄰接對版本,它可能運行得更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.