我是Haskell的新手,我正在尝试在其中实现一些已知的算法。
我已经对字符串实现了合并排序。我有点失望了 与C和Java实现相比,我的Haskell实现的性能。 在我的机器上(Ubuntu Linux,1.8 GHz),C(gcc 4.3.3)在1.85秒内对1 000 000个字符串进行排序, Java(Java SE 1.6.0_14)在3.68秒,Haskell(GHC 6.8.2)在25.89秒。 使用更大的输入(10 000 000个字符串),C需要21.81秒,Java需要59.68秒,Haskell 开始交换,我宁愿在几分钟后停止程序。
由于我是Haskell的新手,我很想知道我的实现是否可以 提高时间/空间效率。
提前感谢您的任何提示 乔治
我的实施:
merge :: [String] -> [String] -> [String]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) = if x < y
then x : (merge xs (y:ys))
else y : (merge (x:xs) ys)
mergeSort :: [String] -> [String]
mergeSort xs = if (l < 2)
then xs
else merge h t
where l = length xs
n = l `div` 2
s = splitAt n xs
h = mergeSort (fst s)
t = mergeSort (snd s)
答案 0 :(得分:18)
试试这个版本:
mergesort :: [String] -> [String]
mergesort = mergesort' . map wrap
mergesort' :: [[String]] -> [String]
mergesort' [] = []
mergesort' [xs] = xs
mergesort' xss = mergesort' (merge_pairs xss)
merge_pairs :: [[String]] -> [[String]]
merge_pairs [] = []
merge_pairs [xs] = [xs]
merge_pairs (xs:ys:xss) = merge xs ys : merge_pairs xss
merge :: [String] -> [String] -> [String]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys)
= if x > y
then y : merge (x:xs) ys
else x : merge xs (y:ys)
wrap :: String -> [String]
wrap x = [x]
编辑:投票给这个答案的人:上面的合并排序实现与ghc Data.List.sort中使用的算法相同,只是删除了cmp函数。那么ghc的作者可能是错的: - /
答案 1 :(得分:14)
在Haskell中,字符串是一个惰性字符列表,并且具有与任何其他列表相同的开销。如果我记得2004年Simon Peyton Jones给出的演讲,那么GHC的空间成本是每个字符40个字节。对于苹果对苹果的比较,您可能应该对Data.ByteString
进行排序,其目的是提供与其他语言相当的性能。
答案 2 :(得分:9)
更好的方法来拆分列表以避免问题CesarB指出:
split [] = ([], [])
split [x] = ([x], [])
split (x : y : rest) = (x : xs, y : ys)
where (xs, ys) = split rest
mergeSort [] = []
mergeSort [x] = [x]
mergeSort xs = merge (mergesort ys) (mergesort zs)
where (ys, zs) = split xs
编辑:修正。
答案 3 :(得分:5)
我不确定这是否是您的问题的原因,但请记住,列表是一个顺序数据结构。特别是,length xs
和splitAt n xs
都会花费一定的时间与列表的长度(O(n)
)成比例。
在C和Java中,您最有可能使用数组,这两个操作都需要一些时间(O(1)
)。
编辑:回答关于如何提高效率的问题,你也可以在Haskell中使用数组。