为什么Scala中的二进制搜索实现如此之慢?

时间:2018-01-26 21:28:51

标签: algorithm scala computer-science binary-search

最近,我实现了这个二进制搜索,它应该在6秒内运行Scala,但它在检查分配的机器上运行12-13秒。

在阅读代码之前请注意:输入包含两行:第一行 - 要搜索的数字列表,第二行 - "搜索术语"在数字列表中搜索。预期输出只列出数字列表中每个术语的索引。每个输入最大长度为10 ^ 5,每个数字最大为10 ^ 9。

例如:

Input:
5 1 5 8 12 13 //note, that the first number 5 indicates the length of the 
following sequence

5 8 1 23 1 11 //note, that the first number 5 indicates the length of the 
following sequence

Output:
2 0 -1 0 -1 // index of each term in the input array

我的解决方案:

object BinarySearch extends App {
  val n_items = readLine().split(" ").map(BigInt(_))
  val n = n_items(0)
  val items = n_items.drop(1)

  val k :: terms = readLine().split(" ").map(BigInt(_)).toList

  println(search(terms, items).mkString(" "))

  def search(terms: List[BigInt], items:Array[BigInt]): Array[BigInt] = {
    @tailrec
    def go(terms: List[BigInt], results: Array[BigInt]): Array[BigInt] = terms match {
      case List() => results
      case head :: tail => go(tail, results :+ find(head))
    }

    def find(term: BigInt): BigInt = {
      @tailrec
      def go(left: BigInt, right: BigInt): BigInt = {
        if (left > right) { -1 }
        else {
          val middle = left + (right - left) / 2
          val middle_val = items(middle.toInt)

          middle_val match {
            case m if m == term => middle
            case m if m <= term => go(middle + 1, right)
            case m if m > term => go(left, middle - 1)
          }
        }
      }

      go(0, n - 1)
    }

    go(terms, Array())
  }
}

是什么让这段代码变得如此之慢?谢谢

1 个答案:

答案 0 :(得分:4)

我担心

的复杂性
results :+ find(head)

将项目附加到长度为L的列表中为O(L)(请参阅here),因此如果要计算n个结果,则复杂度将为O(n * n)。

尝试使用可变ArrayBuffer而不是Array来累积结果,或者只是通过find函数映射输入术语。

换句话说,替换

go(terms, Array())

terms.map( x => find(x) ).toArray

顺便说一句,对问题的限制很小,使用BigInt是过度的,可能会使代码显着变慢。正常的整数应该足够大以解决这个问题。