This question already has answers here:
Why are difference lists more efficient than regular concatenation in Haskell?
(2 answers)
Closed 2 years ago.
Consider the following two execution orders:
a ++ (b ++ c)
and
(a ++ b) ++ c
Why is the first execution order faster than the second?
I'm new to Haskell, hoping for a detailed explanation, thanks!
When fully evaluating the result of x ++ y, you must pay:
The cost of fully evaluating x.
The cost of fully evaluating y.
The cost of traversing x once while executing the (++) operation.
Now let's compare a ++ (b ++ c) and (a ++ b) ++ c. Let's write the cost of evaluating a as CA (and similarly CB, CC), and the cost of traversing a as TA (and similarly TB, TC). Then the cost of fully evaluating a ++ (b ++ c) is:
The cost of a, CA.
The cost of b ++ c, which is
CB
CC
One traversal of b, TB.
TA
This is a grand total of CA+CB+CC+TA+TB. Now, for (a ++ b) ++ c:
The cost of a ++ b, which is
CA
CB
TA
CC
One traversal of a ++ b, which is TA+TB.
This is a grand total of CA+CB+CC+2*TA+TB. Compared to the other order, there is an extra traversal of a, for an extra cost of TA, so this order is more expensive.
I leave it to the reader to try longer chains and start figuring out the pattern. In short, the bad association gives a quadratic amount of traversal as it redoes all the traversals it has already done, plus one more, at each invocation of (++), while the good association traverses each list at most once.
Related
The following algorithm is necessary in a code generation problem that I am tackling. My current algorithm is O(n^2) but I feel like there is a better way to do it.
Suppose I have a predicate function for computing whether x < y.
less?: (x:T, y:T) -> True|False
I know a-priori that this relation is transitive. Such that,
less?(a, b) and less?(b, c)
implies
less?(a, c)
I would like to compute the dependency graph for a set of objects (x1, ..., xn). It should look like:
x1 => (x2, x4, x5)
x2 => (x3)
x5 => (x7)
x10 => ()
etc...
where each node, xi, is associated with a list of xj such that less?(xj, xi) is true. The easiest way to compute this graph is to call less? on all possible pairs of (xi, xj). But the less? relation is expensive and I would like to minimize the calls to less?
Thanks for your help.
-Patrick
If the < relation is sufficiently expensive you might gain by maintaining a matrix in which to store the current state of knowledge about a vs b. We can have a < b, !(a < b), or unknown. Then when you compute a comparison of a vs b, store that in the matrix and look for deductions about a vs c and b vs c for every possible c for which the result is as yet unknown. Do you also have a < b => !(b < a)?
With e.g. a vs b and b vs c there are only a finite number of possibilities to check for compatibility and incompatibility to see where deductions are possible but clearly a < b and b < c => a < c. Because of this we also have a < b and !(a < c) => !(b < c). Perhaps if you write out all possibilities you can find more.
I would be inclined to slowly grow a square of known values, adding new variables one by one chosen in a random order, so at stage i you know the entire contents of the matrix for the first i randomly chosen variables. As you add each new variable I would compare it with the variables already worked on in a random order. You are making every deduction possible. If there is a very clever variable comparison order you might hope that with a random comparison order it will be close enough to the optimal order that you won't be much more inefficient than it.
I have my doubts about this in the worst case. If you never find a < b for any a, b, I think you have to check every possibility.
I'm reading a book Pearls of Functional Algorithm Design. Tried implementing the divide and conquer solution for smallest free number problem.
minfree xs = minfrom 0 (length xs) xs
minfrom a 0 _ = a
minfrom a n xs = if m == b - a
then minfrom b (n-m) vs
else minfrom a (m) us
where b = a + 1 + (n `div` 2)
(us,vs) = partition (<b) xs
m = length us
But this one works no faster than the solution that one might call "naive" solution. Which is
import Data.List ((\\))
minfree' = head . (\\) [0..]
I don't know why this is like this, what's wrong with the divide and conquer algorithm and how to improve it.
Tried using BangPatterns, implementing the version of partition that also returns first list's length in the tuple, so it eliminates additional traversal for m =length us. None of them made improvement.
First one takes more than 5 seconds, whereas second one does it almost instantly in ghci on input [0..9999999].
You have pathological input on which head . (\\) [0..] performs in O(N) time. \\ is defined as follows:
(\\) = foldl (flip delete)
delete x xs is an O(N) operation that removes the first x from xs. foldl (flip delete) xs ys deletes all elements of ys from xs one by one.
In [0..] \\ [0..9999999], we always find the next element to be deleted at the head of the list, so the result can be evaluated in linear time.
If you instead type minfree' (reverse [0..9999999]) into GHCi, that takes quadratic time and you find that it pretty much never finishes.
The divide-and-conquer algorithm on the other hand would not slow down on the reversed input.
The following function:
sortByDist :: (Ord a, Floating a, RealFrac a) => [V2 a] -> Map (V2 a) [V2 a]
sortByDist graph = Map.fromList $ map sort graph where
sort point = (point, sortBy (comparing (distance point)) graph)
Maps each point P on a list to a list of points ordered by their distance to P. So, for example, sortByDist [a, b, c, d] Map.! b is the list [b,a,c,d] if a is the nearest point to b, c is the 2nd nearest, d is the 3rd.
Since it performs a n * log n sort for each element, the complexity is n^2 * log n. This agrees with benchmarks of the time required to sort a list of N points:
points time
200 0m0.086s
400 0m0.389s
600 0m0.980s
800 0m1.838s
1000 0m2.994s
1200 0m4.350s
1400 0m6.477s
1600 0m8.726s
3200 0m39.216s
How much can this be improved theoretically? Is it possible to get it down to N * log N?
As luqui commented, using a quadtree or similar will probably help. Building the tree should take O(n log n): log n passes, each of them O(n) selection and partition. Once you have the tree, you can traverse it to build the lists. The difference between the lists for a node and its children should generally be small, and when some are large, that should tend to force others to be small. Using an adaptive sort (e.g., adaptive merge sort or adaptive splay sort) should thus give good performance, but analyzing the complexity will not be easy. If you want to try to get some sharing, you will have to represent the lists using a sequence type (e.g. Data.Sequence) and then try to figure out relationships between squares at various scales. I have serious doubts about the potential of such an approach to reduce time complexity.
problem is following.
about "nice"
1) "ab" is nice
2) A is nice => "a"+A+"b" is nice
3) A and B are nice => A+B is nice
about "~"
1) "ab"~"ab"
2) A~B => "a"+A+"b"~"a"+B+"b"
3) A~B and C~D => A+C~B+D and A+C~D+B
now there are at most 1000 string of 'a' and 'b' forming a set S, find the biggest subset of S in which every element must be nice and none of pair (A,B) holds A~B. Output the Cardinality.
There are sth different from the problems i see before:
A+B+C+D~A+C+B+D~B+D+A+C but A+B+C+D~B+D+A+C doesn't hold.
Two difficulties for me:
how to check whether S1~S2
if i know every pair's "~", how can i find the cardinality
more detail: https://www.spoj.pl/problems/ABWORDS/
The rules for constructing a nice word imply that every nice word starts with "a" and ends with "b". Hence, there is a unique (up to sequencing - rule 3) decomposition of a nice word into nice sub-words: find all "ab"s, and then try to expand them using rule 2, and sequence them using rule 3. We can express this decomposition via a tree (n branches for repeated application of rule 3).
In the tree context, the "~" relation is simply expressing isomorphic trees, I think (where branch order does not matter).
EDIT: as pointed out, branch order does matter.
I'll attempt to solve the problem as stated in the original link (the 2 definitions of "nice" doesn't coincide).
First, similarity between two words X and Y, using DP.
Define f(a, b, n) to be the function that indicates that X[a..a+2n-1] is similar to Y[b..b+2n-1] and that both subwords are nice.
f(a, b, 0) = 1.
for n > 0,
f(a, b, n) = f1(a, b, n) or f2(a, b, n) or f3(a, b, n)
f1(a, b, n) = x[a] == y[b] == 'a' and x[a+2n-1] == y[b+2n-1] == 'b' and f(a+1, b+1, n-1)
f2(a, b, n) = any(1 <= k < n) f(a, b, k) and f(a+2k, b+2k, n-k)
f3(a, b, n) = any(1 <= k < n) f(a, b+2(n-k), k) and f(a+2k, b, n-k)
I think this is O(n^4) (urgh).
For the second part, if you represent the words as a graph with edges representing the similarity relation, you are essentially trying to find the maximum independent set, I think. If so, good luck! It is NP-hard (i.e. no known better solution than to try all
combinations) in the general case, and I don't see any properties that make it easier in this :(
EDITED to make the definition of similarity automatically check niceness. It is quite easy.
EDITED yet again because of my stupidity.
(This question is related to my previous question, or rather to my answer to it.)
I want to store all qubes of natural numbers in a structure and look up specific integers to see if they are perfect cubes.
For example,
cubes = map (\x -> x*x*x) [1..]
is_cube n = n == (head $ dropWhile (<n) cubes)
It is much faster than calculating the cube root, but It has complexity of O(n^(1/3)) (am I right?).
I think, using a more complex data structure would be better.
For example, in C I could store a length of an already generated array (not list - for faster indexing) and do a binary search. It would be O(log n) with lower сoefficient than in another answer to that question. The problem is, I can't express it in Haskell (and I don't think I should).
Or I can use a hash function (like mod). But I think it would be much more memory consuming to have several lists (or a list of lists), and it won't lower the complexity of lookup (still O(n^(1/3))), only a coefficient.
I thought about a kind of a tree, but without any clever ideas (sadly I've never studied CS). I think, the fact that all integers are ascending will make my tree ill-balanced for lookups.
And I'm pretty sure this fact about ascending integers can be a great advantage for lookups, but I don't know how to use it properly (see my first solution which I can't express in Haskell).
Several comments:
If you have finitely many cubes, put them in Data.IntSet. Lookup is logarithmic time. Algorithm is based on Patricia trees and a paper by Gill and Okasaki.
If you have infinitely many cubes in a sorted list, you can do binary search. start at index 1 and you'll double it logarithmically many times until you get something large enough, then logarithmically many more steps to find your integer or rule it out. But unfortunately with lists, every lookup is proportional to the size of the index. And you can't create an infinite array with constant-time lookup.
With that background, I propose the following data structure:
A sorted list of sorted arrays of cubes. The array at position i contains exp(2,i) elements.
You then have a slightly more complicated form of binary search.
I'm not awake enough to do the analysis off the top of my head, but I believe this gets you to O((log n)^2) worst case.
You can do fibonacci-search (or any other you wuld like) over lazy infinite tree:
data Tree a = Empty
| Leaf a
| Node a (Tree a) (Tree a)
rollout Empty = []
rollout (Leaf a) = [a]
rollout (Node x a b) = rollout a ++ x : rollout b
cubes = backbone 1 2 where
backbone a b = Node (b*b*b) (sub a b) (backbone (b+1) (a+b))
sub a b | (a+1) == b = Leaf (a*a*a)
sub a b | a == b = Empty
sub a b = subBackbone a (a+1) b
subBackbone a b c | b >= c = sub a c
subBackbone a b c = Node (b*b*b) (sub a b) (subBackbone (b+1) (a+b) c)
is_cube n = go cubes where
go Empty = False
go (Leaf x) = (x == n)
go (Node x a b) = case (compare n x) of
EQ -> True
LT -> go a
GT -> go b