给定一个整数列表xs
,让:
count :: [Integer] -> Integer -> Integer
count xs n = length . filter (==n) $ xs
计算列表中出现整数n
的次数。
现在,给出一个"列表" (长度为n
的某种整数数组,可以是除List之外的东西),写一个函数
countSequence :: [Integer] -> Integer -> Integer -> Integer
countSequence xs n m = [count xs x | x <- [0..m]]
输出&#34;计数列表&#34; (第0个索引包含列表中出现0的次数,第1个索引包含列表中出现1次的次数等)具有时间完整性o(m * n)
我给出的上述实现具有复杂度O(m * n)。在Python(我更熟悉)中,在O(m + n)时间内很容易做到这一点 - 遍历列表,每个元素在其他列表中增加一个计数器,初始化为全零和长度(m + 1)。
如何在Haskell中获得更好的实现?如果实现Python解决方案不是一些微不足道的方法(比如在函数中添加另一个输入以保持&#34;计数列表&#34; in然后通过它进行交互),我更喜欢
答案 0 :(得分:2)
在O(n+m)
(有点,我想,也许):
import Data.Ix (inRange)
import qualified Data.IntMap.Strict as IM
countSequence m =
foldl' count IM.empty . filter (inRange (0,m))
where count a b = IM.insertWith (+) b 1 a
给出
> countSequence 2 [1,2,3,1,2,-1]
fromList [(1,2),(2,2)]
我还没有使用n
,因为你也没有使用n
而且我不确定它应该是什么。我还将列表移动到最后一个参数,使其处于减少的位置。
答案 1 :(得分:1)
我认为你应该使用你的Python直觉 - 遍历一个列表并在另一个列表中增加一个计数器。这是O(n + m)运行时的实现:
import Data.Array
countSequence xs m = accumArray (+) 0 (0,m) [(x, 1) | x <- xs, inRange (0,m) x]
(这个用例甚至是文档中存在accumArray
的动机示例!)在ghci中:
> countSequence ([1..5] ++ [1,3..5] ++ [1,4..5] ++ [1,5]) 3
array (0,3) [(0,0),(1,4),(2,1),(3,2)]
答案 2 :(得分:1)
我想使用Data.IntMap
会有效地完成这项工作。完成一次foldr
次传递以建立IntMap
(cm
)和map
以构建一个新列表,其中包含相应位置的元素计数。
import qualified Data.IntMap.Lazy as IM
countSequence :: [Int] -> [Int]
countSequence xs = map (\x -> let cm = foldr (\x m -> IM.alter (\mx -> if mx == Nothing then Just 1 else fmap (+1) mx) x m) IM.empty xs
in IM.findWithDefault 0 x cm) xs
*Main> countSequence [1,2,5,1,3,7,8,5,6,4,1,2,3,7,9,3,4,8]
[3,2,2,3,3,2,2,2,1,2,3,2,3,2,1,3,2,2]
*Main> countSequence [4,5,4]
[2,1,2]
*Main> *Main> countSequence [9,8,7,6,5]
[1,1,1,1,1]