| 1 | {-
|
| 2 |
|
| 3 | Pearl 2: Surpassing
|
| 4 |
|
| 5 | Definitions:
|
| 6 | A surpasser of an element of an array (list) is a greater element to the right.
|
| 7 | x[j] is a surpasser of x[i] if i<j and x[i]<x[j].
|
| 8 | The surpasser count of an element is the number of its surpassers.
|
| 9 |
|
| 10 | Problem:
|
| 11 | Compute the maximum surpasser count of an array of length n>1 in an O(n log n) algorithm.
|
| 12 |
|
| 13 | Solution:
|
| 14 |
|
| 15 | Note 1: In the imperative programming, using iterative+binary search.
|
| 16 | We will use Divide and Conquer
|
| 17 |
|
| 18 | Note 2: Cannot do better than O(n log n), b/c this is equivalen to sorting (you will see:
|
| 19 | the table we create)
|
| 20 | -}
|
| 21 | module P2 where
|
| 22 |
|
| 23 | -- Test word is: GENERATION
|
| 24 | -- Surpasser count is: 5625140100
|
| 25 | -- So the maximum isL 6
|
| 26 | testX = "GENERATION" :: [Char]
|
| 27 |
|
| 28 | -- Maximum Surpasser Count
|
| 29 | -- Brute force approach
|
| 30 | -- Requires O(N^2) steps
|
| 31 | -- Goes on shrinking list of length n: n+(n-1)+(n-2)+.. = n^2/2
|
| 32 | msc :: Ord a => [a] -> Int
|
| 33 | msc xs = maximum [scount z zs | z:zs <- tails xs]
|
| 34 | scount x xs = length (filter (x<) xs)
|
| 35 |
|
| 36 | -- We'll redfine tails, to make sure empty returns empty
|
| 37 | tails :: [a] -> [[a]]
|
| 38 | tails [] = []
|
| 39 | tails (x:xs) = (x:xs):tails xs
|
| 40 |
|
| 41 | -- Divide and Conquer
|
| 42 | -- The trick is to find some 'data' that we can join together
|
| 43 | -- easily. Then we can divide, and then join.
|
| 44 | -- There are a few tricks to get to this 'join':
|
| 45 | -- 1. Define data structure that contains enough information. Then you can join them.
|
| 46 | -- This data structure is the table, that contains not only the Maximum Surpasser from
|
| 47 | -- each divide, but all the counts!
|
| 48 | -- 2. If we keep the Table in sorted order, the join can be done easily! (in Linear time)
|
| 49 |
|
| 50 | table [x] = [(x,0)] -- one element
|
| 51 | table xs = join' (m-n) (table ys) (table zs)
|
| 52 | where
|
| 53 | m = length xs
|
| 54 | n = m `div` 2
|
| 55 | (ys,zs) = splitAt n xs
|
| 56 |
|
| 57 | join' 0 txs [] = txs
|
| 58 | join' n [] tys = tys
|
| 59 | join' n txs@((x,c):txs') tys@((y,d):tys')
|
| 60 | | x<y = (x,c+n) : join' n txs' tys
|
| 61 | | x>=y = (y,d) : join' (n-1) txs tys'
|
| 62 | -- the lists are ordered within themselves.
|
| 63 | -- so if x<y ==> All the rest (and there's length n of tys) are also
|
| 64 | -- greater than x ==> we should add this count!
|
| 65 | -- if x>y, we just put y.
|
| 66 | -- if x==y, we just want to make sure we continue in reduced manner, so we put (y,d), remmove
|
| 67 | -- it from the next join, and then move on to continue.
|
| 68 |
|
| 69 | -- Main
|
| 70 |
|
| 71 | main = do
|
| 72 | putStr "Brute force : "
|
| 73 | print $ msc testX
|
| 74 | putStr "Divide and Conquer: "
|
| 75 | print $ maximum $ map (snd) (table testX)
|