Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 如何在恒定内存中获取make stats_Haskell_Random_Statistics_Memory Management_Lazy Evaluation - Fatal编程技术网

Haskell 如何在恒定内存中获取make stats

Haskell 如何在恒定内存中获取make stats,haskell,random,statistics,memory-management,lazy-evaluation,Haskell,Random,Statistics,Memory Management,Lazy Evaluation,我有一个函数,它创建了一些随机的数值结果。我知道,结果将是a(小,a-b约50)范围a,b的整数。我想创建一个函数,执行上面的函数,比如1000000次,并计算每个结果出现的频率。(该函数需要一个随机生成器来生成结果。)问题是,我不知道如何在恒定内存中进行此操作,而不硬编码范围的长度。我的(坏)方法是这样的: values :: [Int] values = doFunctionNtimes myRandom 1000000 results = map (\x ->length . fil

我有一个函数,它创建了一些随机的数值结果。我知道,结果将是a(小,a-b约50)范围a,b的整数。我想创建一个函数,执行上面的函数,比如1000000次,并计算每个结果出现的频率。(该函数需要一个随机生成器来生成结果。)问题是,我不知道如何在恒定内存中进行此操作,而不硬编码范围的长度。我的(坏)方法是这样的:

values :: [Int]
values = doFunctionNtimes myRandom 1000000
results = map (\x ->length . filter (x==) $ values) [a..b]
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

foldr iw {} [1,2,1,3,2,1]
iw 1 (foldr iw {} [2,1,3,2,1])
iw 1 (iw 2 (foldr iw {} [1,3,2,1]))
iw 1 (iw 2 (iw 1 (foldr iw {} [3,2,1])))
iw 1 (iw 2 (iw 1 (iw 3 (foldr iw {} [2,1]))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 (foldr iw {} [1])))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 (iw 1 (foldr iw {} []))))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 (iw 1 {}))))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 {1 -> 1}))))
iw 1 (iw 2 (iw 1 (iw 3 {1 -> 1, 2 -> 1})))
iw 1 (iw 2 (iw 1 {1 -> 1, 2 -> 1, 3 -> 1}))
iw 1 (iw 2 {1 -> 2, 2 -> 1, 3 -> 1})
iw 1 {1 -> 2, 2 -> 2, 3 -> 1}
{1 -> 3, 2 -> 2, 3 -> 1}
有人有这样的想法吗

编辑:
我想我把问题解释错了,很抱歉。我有一个函数,它根据一个随机的gen,给出一些小的int值。为了进行统计,我想知道结果出现的频率。当我想统计1000000次尝试时,我需要对尝试次数保持恒定的内存。

因此,您有无限数量的可能结果,并想计算每个结果在恒定内存中出现的次数。这显然是不可能做到的,但是调用的数据结构可以用来做一个非常好的近似。在您的情况下,将结果存储在count min草图中,同时分别跟踪最小值和最大值,并在最后查询count min草图中从最小值到最大值的每个整数。

正如Jouni已经提到的,恒定内存是不可能的,但这个count min草图听起来像炸弹!(虽然我以前没听说过)。但我想你可能要求的是将其存储在一个数组中,并且只更新每个频率。这可以在haskell中使用可变数组来完成。以下是一个例子:

main = do gen <- newStdGen
          n <- liftM (read . head) getArgs
          arr  <- (newArray (a,b) 0) :: IO (IOUArray Int Int)
          replicateM_ n $ do 
               result <- myRand
               x <- readArray arr result
               writeArray arr result (x+1)
          (getAssocs arr :: IO [(Int,Int)]) >>= print

我通常处理这类问题的方法是跟踪地图中的计数<代码>数据。IntMap在这种情况下起作用:

import qualified Data.IntMap as I

results :: [Int] -> I.IntMap Int
results = foldr (\x -> I.insertWith (+) x 1) I.empty
此时,您可以询问范围的端点(
I.findMin
I.findMax
),或在O(log n)中查找特定值的计数。为了更快地查找,将所有内容粘贴在一个数组中也非常容易


更新:有关此代码的更好版本,请参阅

import qualified Data.Map as Map
import Data.List (foldl')          -- ' (to fix SO syntax highlighting)

histogram :: (Ord a) => [a] -> Map.Map a Int
histogram = foldl' (\m x -> Map.insertWith' (+) x 1 m) Map.empty
关于为什么这一方法有效以及为什么它优于Travis Brown的解决方案的解释是相当技术性的,需要一些耐心才能完全理解

如果列表中可能出现的值只有有限多个,那么它将在恒定内存中运行。Travis的解决方案有一个微妙的缺陷,其中生成的地图条目如下所示:

(4, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1)
数字19的一种非常低效的表示。只有当你在地图中要求那个元素时,才会计算出这个巨大的总和。这些“thunks”(延迟计算表达式)将随着输入的大小线性增长

为了防止出现这种情况,我们使用了
insertWith'
,它严格应用函数,也就是说它在将结果放入映射之前对结果进行评估。因此,如果您在上面的地图中插入4,它将评估thunk,您将得到一个漂亮的整洁:

(4, 20)
另一个将在添加之前进行评估,因此您将获得:

(4, 21)
所以现在至少地图的值是常量空间

我们需要做的最后一件事是将右折叠更改为左折叠,因为Map.insert在其第二个参数中是严格的。下面演示了右折叠的含义

iw x m = Map.insertWith' (+) x 1 m    -- '

foldr iw Map.empty [1,2,1,3,2,1]
    = iw 1 (iw 2 (iw 1 (iw 3 (iw 2 (iw 1 Map.empty)))))
使用
iw
作为简单的速记
Map.insert
在第二个参数中严格要求,这意味着您需要评估要插入的映射,然后insert才能执行任何操作。我将使用符号
{k1->v1,k2->v2,…}
作为地图的简写。您的评估顺序如下所示:

values :: [Int]
values = doFunctionNtimes myRandom 1000000
results = map (\x ->length . filter (x==) $ values) [a..b]
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

foldr iw {} [1,2,1,3,2,1]
iw 1 (foldr iw {} [2,1,3,2,1])
iw 1 (iw 2 (foldr iw {} [1,3,2,1]))
iw 1 (iw 2 (iw 1 (foldr iw {} [3,2,1])))
iw 1 (iw 2 (iw 1 (iw 3 (foldr iw {} [2,1]))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 (foldr iw {} [1])))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 (iw 1 (foldr iw {} []))))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 (iw 1 {}))))))
iw 1 (iw 2 (iw 1 (iw 3 (iw 2 {1 -> 1}))))
iw 1 (iw 2 (iw 1 (iw 3 {1 -> 1, 2 -> 1})))
iw 1 (iw 2 (iw 1 {1 -> 1, 2 -> 1, 3 -> 1}))
iw 1 (iw 2 {1 -> 2, 2 -> 1, 3 -> 1})
iw 1 {1 -> 2, 2 -> 2, 3 -> 1}
{1 -> 3, 2 -> 2, 3 -> 1}
所以如果你有一个1000000个元素的数组,我们必须一直到第1000000个元素才能开始插入,因此我们需要将之前的999999个元素保留在内存中,这样我们就可以知道接下来要做什么。左折叠解决了这个问题:

-- definition of left fold
foldl' f z xs = go z xs             -- '
    where 
    go accum [] = z
    go accum (x:xs) = accum `seq` go (f accum x) xs

foldl' (flip iw) Map.empty [1,2,1,3,2,1]  -- needed to flip arg order to appease foldl'
go {} [1,2,1,3,2,1]
go (iw 1 {}) [2,1,3,2,1]
go (iw 2 {1 -> 1}) [1,3,2,1]
go (iw 1 {1 -> 1, 2 -> 1}) [3,2,1]
go (iw 3 {1 -> 2, 2 -> 1}) [2,1]
go (iw 2 {1 -> 2, 2 -> 1, 3 -> 1}) [1]
go (iw 1 {1 -> 2, 2 -> 2, 3 -> 1}) []
iw 1 {1 -> 2, 2 -> 2, 3 -> 1}
{1 -> 3, 2 -> 2, 3 -> 1}

现在我们可以看到,最后,如果映射中的条目数是有界的,那么它在恒定的空间和线性时间中运行。

myRandom实际上是RNG吗?如果是这样的话,
值是如何类型为
[Int]
而不是
IO[Int]
MonadRandom r=>r[Int]
或类似的东西的?它看起来不需要任何参数,所以除非有类似的情况,否则它应该只返回重复1000000次的相同值的列表。在这种情况下,您的范围
a,b
Int
的大小限制,这取决于实现,但通常是32位的。Haskell报告说Int应该不小于31位,并且至少有一个编译器使用这个最小值。myRandom只是一个函数,它生活在一个
状态RandomGen
monad中,我不想让描述变得更复杂。@John:是的,O(2**32)是常量,但在类似意义上,你的计算机是一个有限状态机。通常,当人们谈论“常量内存”时,他们希望常量相当小。@Jouni,我大体上同意,除了Haskeller经常混淆
Int
Integer
,并期望前者具有无限精度之外。这就是我提到它的原因。Haskell通常不喜欢类似哈希表的方法,因为它们强烈依赖于杂质。因此,您将失去可组合性,这是Haskell最大的优势之一。通常,您可以根据树或哈希和树的组合来重新构造此类方法。但是CM的作者并没有考虑到杂质的问题,这是一项相当新的工作,所以我怀疑还有其他人做过这件事。您可以在Haskell中使用不纯方法实现它,但在这种情况下,您也可以使用FFI链接到C库。有人使用持久向量实现了Bloom过滤器,CM草图是该方法的推广,因此您可能会做类似的事情:它不会是恒定内存。我很难过,整数的范围为a、b,其中