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
|