給定一個數組,用(+,-, *, /)計算出結果爲N的表達式

我得解法只是簡單的暴力求解,嘗試每一種可能性,把符合條件的結果找出來。應該是有更加高效的算法,至少能夠用到剪枝;僅僅是暴力求解,彷佛沒有拿來作得理由,主要是爲了用scala嘗試functional的解法,經過構建基礎的組件(函數),將它們組合,最後獲得結果;算法

完整的代碼以下:express

object App extends App {

  abstract trait Op {
    def value: Int

    def expression: String

    def isValid: Boolean
  }

  object Op {
    def list(x: Op, y: Op) = List(Add(x, y), Sub(x, y), Mul(x, y), Div(x, y))
  }

  case class Add(left: Op, right: Op) extends Op {
    override def value: Int = left.value + right.value

    override def expression: String = "(" + left.expression + " + " + right.expression + ")"

    override def isValid: Boolean = left.value <= right.value
  }

  case class Sub(left: Op, right: Op) extends Op {
    override def value: Int = left.value - right.value

    override def expression: String = "(" + left.expression + " - " + right.expression + ")"

    override def isValid: Boolean = left.value > right.value
  }

  case class Mul(left: Op, right: Op) extends Op {
    override def value: Int = left.value * right.value

    override def expression: String = left.expression + " * " + right.expression

    override def isValid: Boolean = left.value <= right.value && left.value != 1 && right.value != 1
  }

  case class Div(left: Op, right: Op) extends Op {
    override def value: Int = left.value / right.value

    override def expression: String = left.expression + " / " + right.expression

    override def isValid: Boolean = right.value != 0 && left.value % right.value == 0 && right.value != 1
  }

  case class Val(override val value: Int) extends Op {
    override def expression: String = s"$value"

    override def isValid: Boolean = true
  }

  def splitArray(array: List[Int]): List[(List[Int], List[Int])] =
    array match {
      case Nil => throw new RuntimeException("no empty array allowed here.")
      case x :: y :: Nil => List((List(x), List(y)))
      case h :: tail =>
        (List(h), tail) :: (for {
          (x, y) <- splitArray(tail)
        } yield (h :: x, y))
    }

  def express(nums: List[Int]): List[Op] =
    nums match {
      case Nil => throw new RuntimeException("empty list")
      case x :: Nil => List(Val(x))
      case _ =>
        for {
          (left, right) <- splitArray(nums)
          x <- express(left)
          y <- express(right)
          op <- Op.list(x, y)
          if op.isValid
        } yield op
    }

  def choices(nums: List[Int]): List[List[Int]] = {

    def go(lists: List[List[Int]], i: Int, used: Set[Int]): List[List[Int]] =
      if (i == nums.length) lists
      else {
        for {
          x <- nums
          if (!used(x))
          subChoice <- go(lists ++ lists.map(x :: _), i + 1, used + x)
        } yield {
          subChoice
        }
      }

    go(List(Nil), 0, Set.empty).toSet.toList
  }

  def countDown(nums: Array[Int], res: Int): List[Op] = {
    for {
      list <- choices(nums.toList)
      if (!list.isEmpty)
      op <- express(list)
      if (op.value == res)
    } yield op
  }

  choices(Array(1, 3, 10).toList).foreach(println)

  //  splitArray(Array(1, 3, 7, 10, 25, 50).toList).foreach(println)

  val opList = countDown(Array(1, 3, 7, 10, 25, 50), 765)
  opList.foreach(op => println(op.expression))
}

1. 之因此使用Op trait,主要有兩個做用,a. 咱們不單單是關心最後的值,是否等於765,也關心這個表達式自己;2. 判斷這個表達式是否有效,計算出的結果是不是整數;ide

2. choices(list), 返回用list元素的全部可能的組合,好比choicee(list(1, 2)) => [], [1], [2], [1, 2], [2, 1];接下來能夠用每一個組合去求出Op。 這個方法的時間複雜度即爲n!, 因此能夠看出這個算法只能是玩玩而已;函數

3. 而後用上面求出的組合,去計算表達式,a. 要過濾掉過的集合,由於咱們沒有用來表示空的Op,b. 對於只有一個元素的集合,只有一種可能性,就是該元素自己, Val(x); c. 當有多個元素的時候,須要把該集合切分紅兩邊,遞歸計算Op,而後把Op合併;scala


Just For Fun.code

相關文章
相關標籤/搜索