在 Haskell 中优化基数排序

Posted

技术标签:

【中文标题】在 Haskell 中优化基数排序【英文标题】:Optimizing radix sort in Haskell 【发布时间】:2011-03-11 19:41:51 【问题描述】:

我还在学习 Haskell,我编写了以下基数排序函数。它似乎工作正常,但问题是它的内存效率相当低。如果使用 ghc 编译,内存已经超过 500MB,输入列表大小为 10000 个元素。

所以我想问您如何改进以下算法/代码以使其在速度和内存方面更有效。最好的起点是什么?

import System.Random

-- radixsort for positive integers. uses 10 buckets
radixsort :: [Int] -> [Int]
radixsort [] = []
radixsort xs =
    -- given the data, get the number of passes that are required for sorting
    -- the largest integer
    let maxPos = floor ((log (fromIntegral (foldl max 0 xs)) / log 10) + 1)

        -- start sorting from digit on position 0 (lowest position) to position 'maxPos'
        radixsort' ys pos
         | pos < 0   = ys
         | otherwise = let sortedYs   = radixsort' ys (pos - 1)
                           newBuckets = radixsort'' sortedYs [[] | i <- [1..10]] pos
                       in  [element | bucket <- newBuckets, element <- bucket]

        -- given empty buckets, digit position and list, sort the values into
        -- buckets
        radixsort'' []     buckets _   = buckets
        radixsort'' (y:ys) buckets pos =
            let digit = div (mod y (10 ^ (pos + 1))) (10 ^ pos)
                (bucketsBegin, bucketsEnd) = splitAt digit buckets
                bucket = head bucketsEnd
                newBucket = bucket ++ [y]
            in radixsort'' ys (bucketsBegin ++ [newBucket] ++ (tail bucketsEnd)) pos
    in radixsort' xs maxPos

-- get an random array given an seed
getRandIntArray :: Int -> [Int] 
getRandIntArray seed = (randomRs (0, div (maxBound :: Int) 2) (mkStdGen seed))

main = do
        value <- (\x -> return x ) (length (radixsort (take 10000 (getRandIntArray 0))))
        print value

【问题讨论】:

你考虑过使用 IO monad 中的数组吗? 谢谢,当我对 Haskell 基础知识感到更熟悉时,我一定会检查其他数据类型。 maxPos 中,您应该使用foldl' 而不是foldl。另外,floor (x + 1) 不是更好地表达为ceiling x 吗? 您可以考虑对 Haskell 数组上现有的基数排序进行基准测试:hackage.haskell.org/packages/archive/vector-algorithms/0.4/doc/… @Gabe ST 更好,因为它是准纯的。 【参考方案1】:

主要问题是你的函数radixsort'',因为++ 是 O(n) 并且它每次都复制作为第一个参数给出的列表。

pack (-1) r' _ = r'
pack n  r' relems =
    let getn = (map snd) . (filter ((n==) . fst))
    in pack (n - 1) ((getn relems):r') relems
radixsort'' elems pos = 
    let digit = \y -> div (mod y (10 ^ (pos + 1))) (10 ^ pos)
        relems = zip (map digit elems) elems
    in pack 9 [] relems

【讨论】:

感谢您指出这一点并提供您自己的解决方案。我真的很喜欢你如何巧妙地将值分类到桶中。我有很多东西要学:) ++O(n),其中n 是第一个列表的长度。使用它可能会导致 O(n^2) 算法,而您期望 O(n) 算法。 是的,抱歉我写得太快了。然而,在这个问题中,它被多次使用,导致二次算法。 (固定)

以上是关于在 Haskell 中优化基数排序的主要内容,如果未能解决你的问题,请参考以下文章

基数排序和更改基数

如何优化间接基数排序? (又名如何优化不可预测的内存访问模式)

数据结构-排序之基数排序(使用java代码实现)

数据结构-排序之基数排序(使用java代码实现)

数据结构-排序之基数排序(使用java代码实现)

基数排序