-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcountdown_problem_brute_force.hs
140 lines (115 loc) · 4.7 KB
/
countdown_problem_brute_force.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
----------------------------------------------------------
-- The Countdown Problem Solver - Brute Force Approach --
----------------------------------------------------------
data Op = Add | Sub | Mul | Div
instance Show Op where
show Add = "+"
show Sub = "-"
show Mul = "*"
show Div = "/"
-- | Recursive type which models a numeric expression.
data Expr = Val Int | App Op Expr Expr
instance Show Expr where
show (Val n) = show n
show (App o l r) = bracket l ++ " " ++ show o ++ " " ++ bracket r
where
bracket (Val n) = show n
bracket e = "(" ++ show e ++ ")"
-- List containing all members of Op:
ops :: [Op]
ops = [Add, Sub, Mul, Div]
-- | Checks if the application of an elementary operation to two integers is
-- valid according to the rules of the game.
valid :: Op -> Int -> Int -> Bool
valid Add _ _ = True
valid Sub m n = m > n
valid Mul _ _ = True
valid Div m n = m `mod` n == 0
-- | Applies an elementary operation to two integers.
apply :: Op -> Int -> Int -> Int
apply Add m n = m + n
apply Sub m n = m - n
apply Mul m n = m * n
apply Div m n = m `div` n
-- Some sample expressions:
-- (1 + 50) * (25 - 10)
e :: Expr
e = App Mul (App Add (Val 1) (Val 50)) (App Sub (Val 25) (Val 10))
-- 1 + (2 * 3)
e1 :: Expr
e1 = App Add (Val 1) (App Mul (Val 2) (Val 3))
-- (4 / 2) - 1
e2 :: Expr
e2 = App Sub (App Div (Val 4) (Val 2)) (Val 1)
-- 2 - 3
e3 :: Expr
e3 = App Sub (Val 2) (Val 3)
-- (2 / 3) * 3
e4 :: Expr
e4 = App Mul (App Div (Val 2) (Val 3)) (Val 3)
-- (2 * 3) / 3
e5 :: Expr
e5 = App Div (App Mul (Val 2) (Val 3)) (Val 3)
-- | Returns a list of all the integers appearing in a given expression, prior
-- to its evaluation (and to the evaluation of any subexpressions).
values :: Expr -> [Int]
values (Val n) = [n]
values (App _ l r) = values l ++ values r
-- | Evaluates a given expression. Returns a singleton list whose only entry
-- is the result of the evaluation if the operation is valid, or an empty list
-- if the operation is invalid according to the rules of the game.
eval :: Expr -> [Int]
eval (Val n) = [n | n > 0]
eval (App o l r) = [apply o x y | x <- eval l, y <- eval r, valid o x y]
-- | Given a list, returns all possible sublists which can be formed from it by
-- deleting some of the elements, but preserving the original order.
subs :: [a] -> [[a]]
subs [] = [[]]
subs (x:xs) = (subs xs) ++ (map (x:) (subs xs))
-- | Given x and and a list xs, returns a list of lists comprising all possible
-- ways to obtain a new list by inserting x into xs.
interleave :: a -> [a] -> [[a]]
interleave x [] = [[x]]
interleave x (y:ys) = (x:y:ys) : (map (y:) (interleave x ys))
-- | Given a list, returns a list of lists comprising all permutations of its
-- elements.
perms :: [a] -> [[a]]
perms [] = [[]]
perms (x:xs) = concat (map (interleave x) (perms xs))
-- | Given a list, returns all possible sublists which can be formed by
-- selecting some (or none) of its elements, possibly in a different order.
-- Example: choices [1, 2] = [[], [1], [2], [1, 2], [2, 1]]
choices :: [a] -> [[a]]
choices = concat . map perms . subs
-- | Decides whether a valid expression is a solution of the problem using a
-- given list of numbers (or some sublist of it) for a given target.
isSolution :: Expr -> [Int] -> Int -> Bool
isSolution e ns target = elem (values e) (choices ns) && eval e == [target]
-- | Given a list xs, returns all possible pairs (ls, rs) consisting of
-- sublists which concatenate to xs, in the form of a list of pairs.
split :: [a] -> [([a], [a])]
split [] = []
split [_] = []
split (x:xs) = ([x], xs) : [(x:ls, rs) | (ls, rs) <- split xs]
-- | Returns a list containing all possible combinations of two given
-- expressions using one of the four elementary operations.
combine :: Expr -> Expr -> [Expr]
combine l r = [App o l r | o <- ops]
-- | Given a list of integers, returns a list of all possible expressions
-- obtained by combining them in the given order, but using any possible
-- association and any of the elementary operations at each step.
exprs :: [Int] -> [Expr]
exprs [] = []
exprs [n] = [Val n]
exprs ns = [e | (ls, rs) <- split ns,
l <- exprs ls,
r <- exprs rs,
e <- combine l r]
-- | Given a list of integers and a target, returns the list of all solutions
-- (if any) to the countdown problem for these inputs.
solutions :: [Int] -> Int -> [Expr]
solutions ns n = [e | ms <- choices ns, e <- exprs ms, eval e == [n]]
-- | Prints the number of solutions to the countdown problem given a list of
-- integers and a target.
main :: IO ()
main = print(length(solutions [1, 3, 7, 10, 25, 50] 765))