1 | {-
|
2 |
|
3 | Pearl 4: Selection problem
|
4 |
|
5 | Definitions:
|
6 | X and Y two finite disjoint sets, sorted.
|
7 |
|
8 | Problem:
|
9 | Find the k-smallest elemnt in the set (X union Y)
|
10 |
|
11 | Solution:
|
12 | You can do it of course in linear time O(K).
|
13 | But it turns out you can do faster by divide and conquer.
|
14 | The trick is to do the merge-decisions in the right way.
|
15 |
|
16 | We then also use arrays rather than lists, to get efficency in time.
|
17 |
|
18 | <== And this is where it seems the book messed up on indices. Corrected in my code.
|
19 |
|
20 | I left all the debugging and printing in there, so one can see the equivalence between
|
21 | Array-indices and Lists.
|
22 |
|
23 | -}
|
24 | module P4 where
|
25 |
|
26 | import Data.Array (Array, listArray, bounds, (!))
|
27 | import Debug.Trace
|
28 |
|
29 | -- Test function(s)
|
30 | {-
|
31 | x = [7,14..28]
|
32 | y = [5,10..35]
|
33 | k = 4
|
34 | -}
|
35 | y = [7,14..28]
|
36 | x = [5,10..35]
|
37 | --k = 4
|
38 | k = 7
|
39 |
|
40 | xa = listArray (0,length x-1) x
|
41 | ya = listArray (0,length y-1) y
|
42 |
|
43 | -- Brute force
|
44 | -- Requires (z+1)^2 evaluations of f
|
45 | smallest1 :: Ord a => Int -> ([a],[a]) -> a
|
46 | smallest1 k (xs,ys) = union (xs,ys) !!k
|
47 |
|
48 | union (xs,[]) = xs
|
49 | union ([],ys) = ys
|
50 | union (x:xs,y:ys) | x < y = x : union(xs,y:ys)
|
51 | | x > y = y : union(x:xs,ys)
|
52 |
|
53 |
|
54 |
|
55 | -- Divide and Conquer
|
56 | smallest2 k ([],ws) = ws !! k
|
57 | smallest2 k (zs,[]) = zs !! k
|
58 | smallest2 k (zs,ws) =
|
59 | trace ("zs=" ++ show zs ++
|
60 | " and ws=" ++ show ws ++
|
61 | " and k=" ++ show k ++ "\n" ++
|
62 | " and (p,q)=" ++ show (p,q) ++
|
63 | "and (a,b)=" ++ show(a,b) )$
|
64 | case (a < b, k <= p+q) of
|
65 | (True,True) -> trace "--> (1)" $ smallest2 k (zs,us)
|
66 | (True,False) -> trace "--> (2)" $ smallest2 (k-p-1) (ys,ws)
|
67 | (False,True) -> trace "--> (3)" $ smallest2 k (xs,ws)
|
68 | (False,False) -> trace "--> (4)" $ smallest2 (k-q-1) (zs,vs)
|
69 | where
|
70 | p = (length zs) `div` 2
|
71 | q = (length ws) `div` 2
|
72 | (xs,a:ys) = splitAt p zs
|
73 | (us,b:vs) = splitAt q ws
|
74 |
|
75 |
|
76 | -- Divide and Conquer, using arrays
|
77 |
|
78 | smallest3 :: Ord a => Show a => Int -> (Array Int a, Array Int a) -> a
|
79 | smallest3 k (xa,ya) = search k (0,m+1) (0,n+1)
|
80 | where
|
81 | (0,m) = bounds xa
|
82 | (0,n) = bounds ya
|
83 | -- The below is actually writing smallest2, but using array notation!
|
84 | search k (lx,rx) (ly,ry)
|
85 | | lx==rx = ya ! ly -- x = []
|
86 | | ly==ry = xa ! lx -- y = []
|
87 | | otherwise = trace ("(lx,rx)=" ++ show (lx,rx) ++
|
88 | " and (ly,ry)=" ++ show (ly,ry) ++
|
89 | " and k=" ++ show k ++ "\n" ++
|
90 | " and (mx,my)=" ++ show (mx,my) ++
|
91 | " and (mx',my')=" ++ show (mx',my') ++
|
92 | "and (xa!mx,ya!my)=" ++ show(xa!mx,ya!my) )$
|
93 | case ( (xa!mx) < (ya!my), k <= mx'+my') of
|
94 | (True,True) -> trace "--> (1)" $ search k (lx,rx) (ly,my)
|
95 | (True,False) -> trace "--> (2)" $ search (k-mx'-1) (mx+1,rx) (ly,ry)
|
96 | (False,True) -> trace "--> (3)" $ search k (lx,mx) (ly,ry)
|
97 | (False,False) -> trace "--> (4)" $ search (k-my'-1) (lx,rx) (my+1,ry)
|
98 | where
|
99 | mx = (lx + rx) `div` 2
|
100 | my = (ly + ry) `div` 2
|
101 | mx' = (rx - lx) `div` 2
|
102 | my' = (ry - ly) `div` 2
|
103 |
|
104 | -- Main
|
105 |
|
106 | main = do
|
107 | print $ "x=" ++ (show x)
|
108 | print $ "y=" ++ (show y)
|
109 | print $ "union (x,y)=" ++ (show $ union (x,y))
|
110 | print $ "Looking for element : " ++ (show k) ++ " (remember, indexing starts from 0)"
|
111 | putStr "Brute force : "
|
112 | print $ smallest1 k (x,y)
|
113 | putStrLn "Divide-and-Conquer (w/ lists) : "
|
114 | print $ smallest2 k (x,y)
|
115 | putStr "Divide-and-Conquer (w/ arrays): "
|
116 | print $ smallest3 k (xa,ya)
|