progs/countdown.scala
author Christian Urban <christian.urban@kcl.ac.uk>
Fri, 05 Dec 2025 10:20:00 +0000
changeset 506 28f78d9081d7
parent 505 c06b45a52d50
permissions -rw-r--r--
updated
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
505
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     1
// Countdown Game using the Powerset Function
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     2
//============================================
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     3
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     4
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     5
def powerset(xs: Set[Int]) : Set[Set[Int]] = {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     6
  if (xs == Set()) Set(Set())
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     7
  else {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     8
    val ps = powerset(xs.tail)  
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
     9
    ps ++ ps.map(_ + xs.head)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    10
  }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    11
}  
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    12
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    13
powerset(Set(1,2,3)).mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    14
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    15
// proper subsets
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    16
def psubsets(xs: Set[Int]) = 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    17
  powerset(xs) -- Set(Set(), xs) 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    18
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    19
psubsets(Set(1,2,3)).mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    20
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    21
// pairs of subsets and their "complement"
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    22
def splits(xs: Set[Int]) : Set[(Set[Int], Set[Int])] =
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    23
  psubsets(xs).map(s => (s, xs -- s))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    24
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    25
splits(Set(1,2,3,4)).mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    26
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    27
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    28
// ususal trees representing infix notation for expressions
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    29
enum Tree {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    30
  case Num(i: Int)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    31
  case Add(l: Tree, r: Tree)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    32
  case Mul(l: Tree, r: Tree)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    33
  case Sub(l: Tree, r: Tree)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    34
  case Div(l: Tree, r: Tree)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    35
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    36
import Tree._
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    37
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    38
//pretty printing for trees
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    39
def pp(tr: Tree) : String = tr match {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    40
  case Num(n) => s"$n"
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    41
  case Add(l, r) => s"(${pp(l)} + ${pp(r)})"
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    42
  case Mul(l, r) => s"(${pp(l)} * ${pp(r)})"
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    43
  case Sub(l, r) => s"(${pp(l)} - ${pp(r)})"
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    44
  case Div(l, r) => s"(${pp(l)} / ${pp(r)})"
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    45
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    46
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    47
// evaluating a tree - might fail when dividing by 0 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    48
// (the for-notation makes it easy to deal with Options)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    49
def eval(tr: Tree) : Option[Int] = tr match {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    50
  case Num(n) => Some(n)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    51
  case Add(l, r) => 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    52
    for (ln <- eval(l); rn <- eval(r)) yield ln + rn
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    53
  case Mul(l, r) => 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    54
    for (ln <- eval(l); rn <- eval(r)) yield ln * rn 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    55
  case Sub(l, r) => 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    56
    for (ln <- eval(l); rn <- eval(r); if rn <= ln) yield ln - rn
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    57
  case Div(l, r) => 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    58
    for (ln <- eval(l); rn <- eval(r); if rn != 0) 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    59
      yield ln / rn 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    60
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    61
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    62
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    63
// simple-minded generation of nearly all possible expressions
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    64
// (the nums argument determines the set of numbers over which
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    65
//  the expressions are generated)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    66
def gen1(nums: Set[Int]) : Set[Tree] = nums.size match {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    67
  case 0 => Set()
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    68
  case 1 => Set(Num(nums.head))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    69
  case 2 => {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    70
    val ln = Num(nums.head)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    71
    val rn = Num(nums.tail.head)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    72
    Set(Add(ln, rn), Mul(ln, rn),
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    73
        Sub(ln, rn), Sub(rn, ln),
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    74
        Div(ln, rn), Div(rn, ln))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    75
  }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    76
  case n => {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    77
    val res = 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    78
      for ((ls, rs) <- splits(nums);
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    79
            lt <- gen1(ls);
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    80
            rt <- gen1(rs)) yield {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    81
            Set(Add(lt, rt), Mul(lt, rt),
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    82
                Sub(lt, rt), Sub(rt, lt),
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    83
                Div(lt, rt), Div(rt, lt))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    84
           }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    85
    res.flatten
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    86
  }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    87
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    88
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    89
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    90
// some testcases
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    91
gen1(Set(1))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    92
gen1(Set(1, 2)).mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    93
gen1(Set(1, 2, 3)).map(pp).mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    94
gen1(Set(1, 2, 3)).map(tr => s"${pp(tr)} = ${eval(tr)}").mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    95
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    96
gen1(Set(1, 2, 3)).size             // => 168
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    97
gen1(Set(1, 2, 3, 4, 5, 6)).size    // => 26951040
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    98
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
    99
/*
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   100
    It is clear that gen1 generates too many expressions
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   101
    to be fast overall.
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   102
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   103
    An easy fix is to not generate improper Subs and 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   104
    Divs when they are at the leaves.
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   105
*/
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   106
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   107
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   108
def gen2(nums: Set[Int]) : Set[Tree] =  nums.size match {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   109
  case 0 => Set()
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   110
  case 1 => Set(Num(nums.head))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   111
  case 2 => {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   112
    val l = nums.head
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   113
    val r = nums.tail.head
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   114
    Set(Add(Num(l), Num(r)), 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   115
        Mul(Num(l), Num(r)))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   116
        ++ Option.when(l <= r)(Sub(Num(r), Num(l)))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   117
        ++ Option.when(l > r)(Sub(Num(l), Num(r)))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   118
        ++ Option.when(r > 0 && l % r == 0)(Div(Num(l), Num(r)))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   119
        ++ Option.when(l > 0 && r % l == 0)(Div(Num(r), Num(l)))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   120
  }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   121
  case xs => {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   122
    val res = 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   123
      for ((lspls, rspls) <- splits(nums);
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   124
           lt <- gen2(lspls); 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   125
           rt <- gen2(rspls)) yield {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   126
        Set(Add(lt, rt), Sub(lt, rt),
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   127
            Mul(lt, rt), Div(lt, rt))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   128
    } 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   129
    res.flatten
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   130
  }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   131
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   132
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   133
gen2(Set(1, 2, 3)).size             // => 68
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   134
gen2(Set(1, 2, 3, 4, 5, 6)).size    // => 6251936
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   135
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   136
// OK, the numbers decreased in gen2 as it does 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   137
// not generate leaves like (2 - 3) and (4 / 3).
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   138
// It might still generate such expressions "higher
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   139
// up" though, for example (1 + 2) - (4 + 3)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   140
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   141
println(gen2(Set(1,2,3,4)).map(pp).mkString("\n"))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   142
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   143
// before taking any time measure, it might be good
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   144
// to check that no "essential" expression has been
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   145
// lost by the optimisation in gen2...some eyeballing
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   146
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   147
gen1(Set(1,2,3,4)).map(eval) == gen2(Set(1,2,3,4)).map(eval)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   148
gen1(Set(1,2,3,4,5,6)).map(eval) == gen2(Set(1,2,3,4,5,6)).map(eval)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   149
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   150
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   151
// lets start some timing
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   152
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   153
def time_needed[T](n: Int, code: => T) = {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   154
  val start = System.nanoTime()
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   155
  for (i <- (0 to n)) code
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   156
  val end = System.nanoTime()
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   157
  (end - start) / 1.0e9
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   158
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   159
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   160
// check to reach a target
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   161
def check(xs: Set[Int], target: Int) =
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   162
  gen2(xs).find(eval(_) == Some(target))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   163
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   164
// example 1
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   165
check(Set(50, 5, 4, 9, 10, 8), 560).foreach { sol =>
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   166
  println(s"${pp(sol)} => ${eval(sol)}")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   167
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   168
// => ((50 + ((4 / (10 - 9)) * 5)) * 8) => Some(560)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   169
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   170
time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   171
// => ~14 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   172
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   173
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   174
// example 2
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   175
check(Set(25, 5, 2, 10, 7, 1), 986).foreach { sol =>
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   176
  println(s"${pp(sol)} => ${eval(sol)}")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   177
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   178
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   179
time_needed(1, check(Set(25, 5, 2, 10, 7, 1), 986))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   180
// => ~15 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   181
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   182
// example 3 (unsolvable)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   183
check(Set(25, 5, 2, 10, 7, 1), -1)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   184
time_needed(1, check(Set(25, 5, 2, 10, 7, 1), -1))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   185
// => ~22 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   186
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   187
// example 4
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   188
check(Set(100, 25, 75, 50, 7, 10), 360).foreach { sol =>
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   189
  println(s"${pp(sol)} => ${eval(sol)}")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   190
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   191
time_needed(1, check(Set(100, 25, 75, 50, 7, 10), 360))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   192
// => ~14 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   193
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   194
/*
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   195
  Twenty-two seconds in the worst case...this does not yet 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   196
  look competitive enough.
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   197
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   198
  Lets generate the expression together with the result 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   199
  and restrict the number of expressions in this way.
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   200
*/
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   201
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   202
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   203
def gen3(nums: Set[Int]) : Set[(Tree, Int)] =  nums.size match {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   204
  case 0 => Set()
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   205
  case 1 => Set((Num(nums.head), nums.head))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   206
  case xs => {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   207
    val res =
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   208
      for ((lspls, rspls) <- splits(nums);
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   209
           (lt, ln) <- gen3(lspls); 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   210
           (rt, rn) <- gen3(rspls)) yield {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   211
        Set((Add(lt, rt), ln + rn),
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   212
            (Mul(lt, rt), ln * rn))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   213
        ++ Option.when(ln <= rn)((Sub(rt, lt), rn - ln)) 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   214
        ++ Option.when(ln > rn)((Sub(lt, rt), ln - rn))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   215
        ++ Option.when(rn > 0 && ln % rn == 0)((Div(lt, rt), ln / rn))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   216
        ++ Option.when(ln > 0 && rn % ln == 0)((Div(rt, lt), rn / ln))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   217
    } 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   218
    res.flatten
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   219
  }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   220
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   221
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   222
// eyeballing that we did not lose any solutions...
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   223
gen2(Set(1,2,3,4)).map(eval).flatten == gen3(Set(1,2,3,4)).map(_._2)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   224
gen2(Set(1,2,3,4,5,6)).map(eval).flatten == gen3(Set(1,2,3,4,5,6)).map(_._2)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   225
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   226
// the number of generated expressions
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   227
gen3(Set(1, 2, 3)).size             // => 104
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   228
gen3(Set(1, 2, 3))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   229
gen3(Set(1, 2, 3, 4, 5, 6)).size    // => 5092300
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   230
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   231
// ...while this version does not "optimise" the leaves
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   232
// as in gen2, the number of generated expressions grows
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   233
// slower
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   234
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   235
def check2(xs: Set[Int], target: Int) =
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   236
  gen3(xs).find(_._2 == target)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   237
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   238
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   239
// example 1
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   240
time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), 560))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   241
// => ~14 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   242
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   243
// example 2
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   244
time_needed(1, check2(Set(25, 5, 2, 10, 7, 1), 986))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   245
// => ~18 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   246
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   247
// example 3 (unsolvable)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   248
time_needed(1, check2(Set(25, 5, 2, 10, 7, 1), -1))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   249
// => ~19 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   250
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   251
// example 4
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   252
time_needed(1, check2(Set(100, 25, 75, 50, 7, 10), 360))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   253
// => ~16 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   254
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   255
// ...we are getting better, but not yet competetive enough.
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   256
// 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   257
// The problem is that splits generates for sets say {1,2,3,4}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   258
// the splits ({1,2}, {3,4}) and ({3,4}, {1,2}). This means that
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   259
// we consider terms (1 + 2) * (3 + 4) and (3 + 4) * (1 + 2) as
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   260
// separate candidates. We can avoid this duplication by returning
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   261
// sets of sets of numbers, like 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   262
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   263
// pairs of subsets and their "complement"
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   264
def splits2(xs: Set[Int]) : Set[Set[Set[Int]]] =
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   265
  psubsets(xs).map(s => Set(s, xs -- s))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   266
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   267
splits(Set(1,2,3,4)).mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   268
splits2(Set(1,2,3,4)).mkString("\n")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   269
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   270
def gen4(nums: Set[Int]) : Set[(Tree, Int)] =  nums.size match {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   271
  case 0 => Set()
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   272
  case 1 => Set((Num(nums.head), nums.head))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   273
  case xs => {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   274
    val res =
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   275
      for (spls <- splits2(nums);
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   276
           (lt, ln) <- gen4(spls.head); 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   277
           (rt, rn) <- gen4(spls.tail.head)) yield {
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   278
        Set((Add(lt, rt), ln + rn),
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   279
            (Mul(lt, rt), ln * rn))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   280
        ++ Option.when(ln <= rn)((Sub(rt, lt), rn - ln)) 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   281
        ++ Option.when(ln > rn)((Sub(lt, rt), ln - rn)) 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   282
        ++ Option.when(rn > 0 && ln % rn == 0)((Div(lt, rt), ln / rn))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   283
        ++ Option.when(ln > 0 && rn % ln == 0)((Div(rt, lt), rn / ln)) 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   284
    } 
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   285
    res.flatten
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   286
  }
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   287
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   288
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   289
// eyeballing that we did not lose any solutions...
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   290
gen4(Set(1,2,3,4)).map(_._2) == gen3(Set(1,2,3,4)).map(_._2)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   291
gen4(Set(1,2,3,4,5,6)).map(_._2) == gen3(Set(1,2,3,4,5,6)).map(_._2)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   292
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   293
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   294
gen4(Set(1, 2, 3)).size             // => 43
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   295
gen4(Set(1, 2, 3, 4))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   296
gen4(Set(1, 2, 3, 4, 5, 6)).size    // => 550218
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   297
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   298
def check3(xs: Set[Int], target: Int) =
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   299
  gen4(xs).find(_._2 == target)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   300
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   301
// example 1
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   302
check3(Set(50, 5, 4, 9, 10, 8), 560).foreach { sol =>
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   303
  println(s"${pp(sol._1)} => ${eval(sol._1)}")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   304
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   305
// (((10 - 5) * (9 * 8)) + (4 * 50)) => Some(560)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   306
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   307
time_needed(1, check3(Set(50, 5, 4, 9, 10, 8), 560))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   308
// => ~1 sec
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   309
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   310
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   311
// example 2
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   312
check3(Set(25, 5, 2, 10, 7, 1), 986).foreach { sol =>
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   313
  println(s"${pp(sol._1)} => ${eval(sol._1)}")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   314
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   315
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   316
time_needed(1, check3(Set(25, 5, 2, 10, 7, 1), 986))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   317
// => ~1 sec
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   318
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   319
// example 3 (unsolvable)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   320
check3(Set(25, 5, 2, 10, 7, 1), -1)
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   321
time_needed(1, check3(Set(25, 5, 2, 10, 7, 1), -1))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   322
// => ~2 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   323
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   324
// example 4
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   325
check3(Set(100, 25, 75, 50, 7, 10), 360).foreach { sol =>
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   326
  println(s"${pp(sol._1)} => ${eval(sol._1)}")
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   327
}
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   328
time_needed(1, check3(Set(100, 25, 75, 50, 7, 10), 360))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   329
// => ~1 secs
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   330
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   331
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   332
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   333
time_needed(1, check3(Set(50, 5, 4, 9, 10, 8), 560))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   334
time_needed(1, check3(Set(25, 5, 2, 10, 7, 1), 986))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   335
time_needed(1, check3(Set(25, 5, 2, 10, 7, 1), -1))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   336
time_needed(1, check3(Set(100, 25, 75, 50, 7, 10), 360))
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff changeset
   337