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)
場所x <= M & y < x
で(y,x)
も生成されます)。
比較ツリーの最上部に非常に近い、生成されたいくつかの最初の値のそれぞれについて、実質的に O(1) になるはずです。
Prelude Main> take 10 $ map 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> take 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 バイト)
プレリュード 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> let x=it
*メイン> zipWith (>=) x (テール x)
[True,True,True,True,True,True,True,True,True]
Prelude Main> logBase 2 (0.36/0.17)
1.082462160191973 -- O(n^1.08) for n=100000 値が生成される
これは、ここで見られるように、Haskell ストリームのジェネレーターを使用して、たとえば Python に簡単に変換できます。