1 
{-
  2 
 
  3 
Pearl 1: Smallest Free Number
  4 
 
  5 
Problem:
  6 
Assume X is a list of distinct Natural numbers (non-negative integers), in no particular order.
  7 
Find the smallest number not in the list.
  8 
 
  9 
We will show two algorithms that do it in LINEAR time.
 10 
On first sight, it is not clear such exist. After all, sorting a list is no linear time.
 11 
But there's a trick.
 12 
 
 13 
The key trick: not every number in the range [0..length xs] can be in xs!
 14 
Thus, the smallest number NOT in xs is then the smallest number not in
 15 
filter (<=n) xs, where n=length xs.
 16 
 
 17 
The two programs:
 18 
Array based: builds a checklist of the numbers present in filter (<=n) xs.
 19 
 
 20 
Divide and conquer:
 21 
express minfree(xs++ys) in terms of minfree xs  and minfree ys.
 22 
 
 23 
-}
 24 
module P1 where
 25 
 
 26 
import Data.Array     (Array, elems, accumArray)
 27 
import Data.List     (partition)
 28 
testX = [8,23,9,0,12,11,1,10,13,7,41,21,5,17,3,19,2,6] :: [Int]
 29 
 
 30 
type Nat = Int     -- Just for ease of reading things here. Not correct!!
 31 
 
 32 
-- Brute force approach
 33 
-- Requires O(N^2) steps
 34 
-- (see implemntation of (\\) )
 35 
minfree :: [Nat] -> Nat
 36 
minfree xs = head ([0..] \\ xs)
 37 
 
 38 
-- where  us (\\) vs    denotes the list of those elements in us remaining after removing
 39 
-- any elements in vs.
 40 
(\\)    :: Eq a => [a] -> [a] -> [a]
 41 
us \\ vs = filter ( `notElem` vs) us
 42 
 
 43 
-- Array based method: Build the checklist as an array
 44 
-- We will use the 'tricky' function accumArray, which will enable us to do it in linear time.
 45 
-- accumArray: Constructs an immutable array from a list of associations.
 46 
-- Unlike array, the same index is allowed to occur multiple times in the list of associations;
 47 
-- an accumulating function is used to combine the values of elements with the same index.
 48 
 
 49 
checkList         :: [Nat] -> Array Nat Bool
 50 
checkList xs     =  accumArray         --
 51 
                    (||)             -- accumlating function. not realy used, as no duplicates!
 52 
                    False            -- inital entry for each index
 53 
                    (0,n)            -- (lower/upper) indices of new array
 54 
                    (zip (filter (<= n) xs) (repeat True) )    -- index-value pairs
 55 
                    where
 56 
                        n = length xs
 57 
-- Just for fun: Another use of accumArray: Sorting array in LINEAR time, if we know all
 58 
-- values lie between (0,n)
 59 
-- in our case, we can then look for the first '0' in the resulting array
 60 
 
 61 
countList        ::  [Nat] -> Array Nat Nat
 62 
countList xs    =   accumArray (+) 0 (0,n) ( zip xs (repeat 1))
 63 
                    where
 64 
                        n = length xs
 65 
 
 66 
-- Once we have the array, finding the minimum is simple:
 67 
-- (elems converts array into a list)
 68 
search :: Array Nat Bool -> Nat
 69 
search = length.takeWhile id.elems
 70 
 
 71 
-- Divide and conquer
 72 
minFree xs            =    minFrom 0 (length xs,xs)
 73 
minFrom a (n,xs)    |    n==0        = a
 74 
                    |    m==b-a        = minFrom b (n-m,vs)    -- if us has exactly the number of spots,
 75 
                                                            -- and since there's no repetiion, it means
 76 
                                                            -- the gap have to be in the OTHER series:
 77 
                                                            -- so we continue the search with vs
 78 
                    |    otherwise    = minFrom a (m,us)        -- if us had the gap ==> keep searching there!
 79 
                        where
 80 
                            (us,vs) = partition (<b) xs        -- partitioning to 'two halves'
 81 
                            b         = a + 1 + n `div` 2     -- b is the middle of [a..n]
 82 
                            m         = length us
 83 
 
 84 
-- Main
 85 
 
 86 
main = do
 87 
    putStr "Brute force       : "
 88 
    print $ minfree testX
 89 
    putStr "Array method      : "
 90 
    print $ search $ checkList testX
 91 
    putStr "Divide and Conquer: "
 92 
    print $ minFree testX