In Haskell, it produces output immediately . Here is an illustration:
------- -*------ -**------ -***------ -****------ -*****------ -******------ -*******------
Each marked point creates both (x, y) and (y, x). The algorithm "eats" this thing from the upper right corner, comparing the upper elements in each column. The length of the boundary does not exceed N (we assume N >= M ).
enuNM nm | n<m = enuNM mn -- make sure N >= M enuNM nm = 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 fz [] = z foldi fz (x:xs) = fx (foldi fz (pairs f xs)) pairs f (x:y:t) = fxy : pairs ft pairs ft = t
foldi creates a skewed infinitely deepening tree that serves as a heap, combining all producer flows, each for each x , which are created already sorted in descending order. Since all initial values of producer flows are guaranteed to be in descending order, each initial value can be pushed without comparison, which allows you to build a tree lazily.
Code a creates pairs above the diagonal line using the corresponding pairs from the bottom of the diagonal line (assuming N >= M , for each (x,y) , where x <= M & y < x , (y,x) should also be produced. )
It should be practically O (1) for each of the first few values that are very close to the top of the comparison tree.
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 secs , 1045144 bytes)
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 secs , 2094232 bytes)
We can evaluate the empirical complexity at runtime:
Prelude Main> take 10 $ drop 50,000 $ 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 secs , 35425848 bytes)
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 secs , 71346352 bytes)
* Main> let 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) for n = 100000 values produced
This can be translated, for example, using Python generators for Haskell threads, as can be seen here .