Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most efficient purely functional algorithm for generating all prefixes of a list?

prefixes ls = zipWith take [1 .. length ls] (repeat ls)

Is there any way to do better than this? Intuitively, it seems to me that one can't get an algorithm below O(n²) in a purely functional language because either reverse or append must be applied n times. I have no idea how to prove this, though.

like image 763
Matthew Leon Avatar asked Nov 27 '10 06:11

Matthew Leon


2 Answers

I think you are right. There can be no sharing of the spine of the list because all the tails are different. Therefore the list of prefixes, if fully evaluated, would take the full Θ(n2) space, which must take Ω(n2) time to generate.

Note that (a lazier version of) the function you wrote is available in Data.List as inits.

There is a neat optimization you can do though. This equation holds:

map (foldl f z) . inits = scanl f z

And scanl runs in linear time. So if you can phrase the thing you want to do to each prefix as a left fold, then you can avoid the quadratic complexity of building the list of prefixes.

like image 121
luqui Avatar answered Oct 22 '22 06:10

luqui


Isn't that representation-dependent? If you represent lists as contiguous storage plus start and end index (similar to bytestrings), you can share the storage and just need to traverse once to build the list of indices. The algorithm would not change, just the representation. For this particular use case, using snoc lists (binary lists, but nested from the end, not the start, of the list) would also allow for sharing of sublists, right?

like image 3
claus Avatar answered Oct 22 '22 07:10

claus