快速计算列表中的已知元素

时间:2013-11-10 00:32:51

标签: haskell

我正在尝试为Hackerrank的一个问题编写解决方案。挑战是计算列表中的元素,元素从0到99不等,因此可以在线性时间内计算它们。这是我得到的:

{-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -O3 #-}

module Main where

import Data.STRef
import Data.Foldable
import Control.Monad
import Control.Monad.ST

main = do

 line1 <- getLine
 line2 <- getLine

 let
    !ns = map read $ words line2 :: [Int]

    res = runST $ do

        refs <- forM [0..99] $ \i ->
            newSTRef (0 :: Int) 

        traverse_ (\x -> modifySTRef' (refs !! x) (+1) ) ns

        mapM (\ref -> readSTRef ref) refs

 putStrLn . unwords . map show $ res

此代码有效,但速度不够快,无法通过最后一个测试用例。有人可以推荐一项改进吗? (link to the problem

2 个答案:

答案 0 :(得分:6)

这可以使用Data.Array中的accumArray作为一行进行。类似于accumArray (+) 0 (0,99) . zip values $ repeat 1,其中values是输入。

它似乎仍然不够快,这有点令人烦恼。 accumArray或多或少尽可能高效。在我的系统上进行测试显示处理1,000,000个输入值的时间约为1秒,即使没有编译也是如此,并且该时间主要是生成随机输入。这与测试网站上的5秒相差甚远。我不得不怀疑该系统是否过载。

答案 1 :(得分:3)

您遇到的一个问题是,您在列表中查找STRef,这意味着每次查找和修改都需要遍历O(n)步骤。这可以通过使用Data.Map.Map具有O(log(n))查找和修改时间的内容来缓解。

您还可以在Array monad中使用可变VectorO(1) ST查找/修改时间。这可能是最快的方法。