| 1 | {-
|
| 2 | From "Pearls of Functional Algorithm Design", by Richard Bird
|
| 3 |
|
| 4 | Chapter 9.
|
| 5 |
|
| 6 | ** Problem: Celebrity Clique.
|
| 7 |
|
| 8 | Set of P people in a Party.
|
| 9 | Subset C of P is called "Celebrity Clique" if it's not empty,
|
| 10 | and everyone in the party knows every member of C, but members
|
| 11 | C know only each other.
|
| 12 |
|
| 13 | Assumin gthere is such a clique, we need to find it.
|
| 14 |
|
| 15 | We are given the set P (list, not containing duplicates),
|
| 16 | and a binary predicate 'knows'.
|
| 17 |
|
| 18 | ** Formaulation of the problem
|
| 19 | Set annotation:
|
| 20 | C is celebrity-clique of P if
|
| 21 | C \in P, C \not empty , and
|
| 22 | { \forevery x \in P, \forevery y \in C ::
|
| 23 | x `knows` y AND ( y `knows` x ==> x \in C )}
|
| 24 |
|
| 25 | ** Solution (Brute force):
|
| 26 | Create all subsequences.
|
| 27 | For each subsequence, check if the condition above holds.
|
| 28 |
|
| 29 | ** Solution (in Linear time)
|
| 30 | See the book for full explanation. The point: We know there IS a clique.
|
| 31 | we just need to find it.
|
| 32 | -}
|
| 33 |
|
| 34 | module P9 where
|
| 35 | -- For our party, we will have a set of 6 people.
|
| 36 | -- The celebrity clique will be 1 and 3.
|
| 37 | ps = [1..6] :: [Int]
|
| 38 | aKnowsb :: [(Int,Int)]
|
| 39 | aKnowsb = [ (2,1),(3,1),(4,1),(5,1),(6,1)] ++
|
| 40 | [ (1,3),(2,3),(4,3),(5,3),(6,3)] ++
|
| 41 | [ (2,4),(5,4)]
|
| 42 |
|
| 43 | knows :: Int -> Int -> Bool
|
| 44 | knows x y = (x,y) `elem` aKnowsb
|
| 45 |
|
| 46 | --------------------
|
| 47 | -- Brute force solution
|
| 48 | cclique :: [Int] -> [Int]
|
| 49 | cclique ps = head (filter (\x -> isCC x ps) (subseqs ps))
|
| 50 |
|
| 51 | -- is this subgroup a celebrity-clique?
|
| 52 | isCC :: [Int] -> [Int] -> Bool
|
| 53 | isCC cc ps = and [ (x `knows` y) &&
|
| 54 | ( (not (y `knows` x) ) || ((y `knows` x) && (x `elem` cc)) )
|
| 55 | | x<-ps , y<-cc, x /=y]
|
| 56 |
|
| 57 | -- All possible subsequences, in descending order of length
|
| 58 | -- (longest one first)
|
| 59 | subseqs :: [Int] -> [[Int]]
|
| 60 | subseqs [] = [[]]
|
| 61 | subseqs (x:xs) = map (x:) (subseqs xs) ++ subseqs xs
|
| 62 |
|
| 63 |
|
| 64 | --------------------
|
| 65 | -- Linear solution
|
| 66 |
|
| 67 | -- just noting. This is the base for the fusion work.
|
| 68 | -- Not used here, but used in the derivation.
|
| 69 | subseqs' :: [Int] -> [[Int]]
|
| 70 | subseqs' xs = foldr add [[]] xs
|
| 71 |
|
| 72 | add :: Int -> [[Int]] -> [[Int]]
|
| 73 | add x xss = map (x:) xss ++ xss
|
| 74 |
|
| 75 | cclique' :: [Int] -> [Int]
|
| 76 | cclique' ps = foldr op [] ps
|
| 77 |
|
| 78 | -- cs is the current clique. p is the new person
|
| 79 | op :: Int -> [Int] -> [Int]
|
| 80 | op p cs | null cs = [p] -- We know there's a clique, so if it's
|
| 81 | -- empty so far, p is 'it'.
|
| 82 | | not (p `knows` c) = [p] -- if the new person doesn't know c,
|
| 83 | -- cs is blown off, and we start afresh.
|
| 84 | | not (c `knows` p) = cs -- if we got here, that means that p knows c
|
| 85 | -- now, if c doesn't know p, we are good with cs
|
| 86 | -- as is!
|
| 87 | | otherwise = p:cs -- and if c does know p, then p is part
|
| 88 | -- of the clique.
|
| 89 | where
|
| 90 | c = head cs
|
| 91 | -- This is the tricky part here: How come we comapre only to the head of the list?!?
|
| 92 | -- The reason: We KNOW there is a clique. So if it works the basic condition, we say this
|
| 93 | -- is it. B/C if this is not the right clique, we are bound to find this shortly.
|
| 94 | -- This is what they mention: We get linear-time by KNOWING there is a clique. If we had
|
| 95 | -- to consider also the non-clique case, we would have to check it through...
|
| 96 | --
|
| 97 | main = do
|
| 98 | putStr "Direct Brute-Force method: "
|
| 99 | print $ cclique' ps
|
| 100 | putStr "Linear time method : "
|
| 101 | print $ cclique' ps
|