在 Haskell 中,它立即产生输出。这是一个插图:
-------
-*------
-**------
-***------
-****------
-*****------
-******------
-*******------
每个星号点都会产生 (x,y) 和 (y,x)。该算法从右上角“吃掉”这个东西,比较每一列中的顶部元素。边界的长度永远不会超过N
(我们假设N >= M
)。
enuNM n m | n<m = enuNM m n -- make sure N >= M
enuNM n m = let
b = [ [ (x*y,(x,y)) | y<- [m,m-1..1]] | x<-[n,n-1..m+1]]
a = [ (x*x,(x,x)) :
concat [ [(z,(x,y)),(z,(y,x))] -- two symmetrical pairs,
| y<- [x-1,x-2..1] -- below and above the diagonal
, let z=x*y ] | x<-[m,m-1..1]]
in
foldi (\(x:xs) ys-> x : merge xs ys) [] (b ++ a)
merge a@(x:xs) b@(y:ys) = if (fst y) > (fst x)
then y : merge a ys
else x : merge xs b
merge a [] = a
merge [] b = b
foldi f z [] = z
foldi f z (x:xs) = f x (foldi f z (pairs f xs))
pairs f (x:y:t) = f x y : pairs f t
pairs f t = t
foldi
构建一个倾斜的无限加深的树作为一个堆,连接所有的生产者流,每个生产者流,每个生产者流x
已经按降序排序。由于生产者流的所有初始值都保证按降序排列,因此每个初始值都可以在不进行比较的情况下弹出,从而可以懒惰地构建树。
的代码a
使用对角线下方的相应对生成对角线上方的对(在假设下N >= M
,对于每个(x,y)
where x <= M & y < x
,(y,x)
也将生成。)
对于非常接近比较树顶部的少数第一个值中的每一个,它实际上应该是 O(1)。
Prelude Main> 取 10 $ 地图 snd $ enuNM (2000) (3000)
[(3000,2000),(2999,2000),(3000,1999),(2998,2000),(2999,1999),(3000,1998),(2997,2
000),(2998,1999),(2999,1998),(2996,2000)]
(0.01 秒,1045144 字节)
Prelude Main> let xs=take 10 $map (log.fromIntegral.fst) $ enuNM (2000) (3000)
Prelude Main> zipWith (>=) xs (tail xs)
[True,True,True,True,True,True,True,True,True]
Prelude Main> 取 10 $ map snd $ enuNM (2*10^8) (3*10^8)
[(300000000,200000000),(299999999,200000000),(300000000,199999999),(299999998,20
0000000),(299999999,199999999),(300000000,199999998),(299999997,200000000),(2999
99998,199999999),(299999999,199999998),(299999996,200000000)]
(0.01 秒,2094232 字节)
我们可以评估经验运行时复杂度:
Prelude Main> take 10 $ drop 50000 $ map (log.fromIntegral.fst) $ enuNM (2*10^8)
(3*10^8)
[38.633119670465554,38.633119670465554,38.63311967046555,38.63311967046554,38.63
311967046554,38.63311967046553,38.63311967046553,38.633119670465526,38.633119670
465526,38.63311967046552]
(0.17 秒,35425848 字节)
Prelude Main> take 10 $ drop 100000 $ map (log.fromIntegral.fst) $ enuNM (2*10^8
) (3*10^8)
[38.63311913546512,38.633119135465115,38.633119135465115,38.63311913546511,38.63
311913546511,38.6331191354651,38.6331191354651,38.633119135465094,38.63311913546
5094,38.63311913546509]
(0.36 秒,71346352 字节)
*Main> 让 x=it
*Main> zipWith (>=) x (tail x)
[True,True,True,True,True,True,True,True,True]
Prelude Main> logBase 2 (0.36/0.17)
1.082462160191973 -- O(n^1.08) 产生 n=100000 个值
这可以以一种简单的方式翻译成 Python,例如使用 Haskell 流的生成器,如这里所示。