我正在实现一个近似计数算法,其中:
使用 log(log n)位维护计数器X
将计数器X初始化为0
当一个项目到达时,以概率(½)X将X增加1
当流结束时,输出2X - 1,使得E[2X] = n + 1
我的实现如下:
import System.Random
type Prob = Double
type Tosses = Int
-- * for sake of simplicity we assume 0 <= p <= 1
tos :: Prob -> StdGen -> (Bool,StdGen)
tos p s = (q <= 100*p, s')
where (q,s') = randomR (1,100) s
toses :: Prob -> Tosses -> StdGen -> [(Bool,StdGen)]
toses _ 0 _ = []
toses p n s = let t@(b,s') = tos p s in t : toses p (pred n) s'
toses' :: Prob -> Tosses -> StdGen -> [Bool]
toses' p n = fmap fst . toses p n
morris :: StdGen -> [a] -> Int
morris s xs = go s xs 0 where
go _ [] n = n
go s (_:xs) n = go s' xs n' where
(h,s') = tos (0.5^n) s
n' = if h then succ n else n
main :: IO Int
main = do
s <- newStdGen
return $ morris s [1..10000]
问题在于,对于任何
|stream| > 2
,我的 X 总是不正确,并且似乎对于所有的 StdGen
和 |stream| > 1000
,X = 7
。我在 Matlab 中测试了相同的算法,它在那里运行良好,所以我认为要么是:
1.我的随机数生成器存在问题;或者 2.在
Double
中将1/2提高到一个大的n请建议下一步操作。
0.5^2000 :: Double
是零,但我看不出这会在这里引起麻烦。 - chiStdGen
是容易出错的,因为很容易使用旧的或两次使用新的。话虽如此,就我所见,您的代码似乎正确地传递了它们。为了防止这些陷阱,在将来考虑使用Control.Monad.Random
中的Rand
单子。 - chi