如何在Scala中的间隔上通过二进制搜索找到最小值?

时间:2018-07-02 14:44:43

标签: scala binary-search minimum

好吧,我试图在Scala中用二进制搜索找到给定间隔的最小值,但是最小值必须匹配一个给定的函数。 这是我的代码:

def binarySearch: (Int => Boolean) => (Int, Int) => Int = f => (l, h) => {
  def bs: ((Int => Boolean) => (Int, Int, Int) => Int) = f => (l, h, minimum) => {

    val mid = l + ((h-l) / 2)

    mid match{
      case mid if(f(mid) == false) => bs(f)(mid+1, h, mid)
      case mid if(f(mid) == true && mid > minimum) => bs(f)(l, mid-1, minimum)
      case mid if(f(mid) == true && mid < minimum) => bs(f)(mid+1, h, mid)
      case mid => mid
    }
  }
  bs(f)(l, h, 0)
}

我认为我的问题是,我没有正确保存最小值。

一个测试用例可能看起来像这样:

val v = Vector(0, 1, 2, 3, 7, 9)
binarySearch(v(_) < 5)(0, v.length) == 4

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

这种声明方法的样式非常不寻常,使您的代码难以阅读和理解。因此,我的第一步是以一种更加惯用的,传统的方式重现您的代码。

我采取的步骤如下:

  1. 恕我直言,函数最好接受参数,而不是返回带有参数的匿名函数。它更简单,更清洁,使您的意图更清晰。
  2. 命名 predicate 函数是一种惯例,这些函数通常采用单个参数并返回Boolean-p
  3. bs中,其谓词函数参数的重新声明是多余的;它可以重新使用提供给binarySearch的谓词,因此不需要重新声明它。
  4. 测试Boolean值时,通常让该值保持不变。也就是说,如果boolExpr是具有值Booleantrue的{​​{1}}表达式,则相对于false和{{1 }}到if(boolExpr)
  5. 如果前面的情况都不匹配,则可以使用默认情况下的if(boolExpr == true)。这比您代码中的if(!boolExpr)更加清楚。

您的原始代码将变为:

if(boolExpr == false)

好的,现在我们来调试您的功能。您声称它应计算给定间隔的最小值,并且最小值必须与也给定的函数匹配。或者,换句话说,它应该在满足谓词的范围内找到最小值。

但是,在您的测试用例中,您的断言是该值必须小于5,并且您希望该值4作为答案。

一些问题很明显:

  1. 您的代码假设但不验证数据从低到高排序。我之所以这样说是因为,显然,如果数据的排序不正确,代码将失败。如果您可以保证该假设成立,那很好。
  2. case _case middef binarySearch(p: Int => Boolean)(l: Int, h: Int): Int = { def bs(l: Int, h: Int, minimum: Int): Int = { val mid = l + ((h - l) / 2) mid match { case mid if(!p(mid)) => bs(mid+1, h, mid) case mid if(p(mid) && mid > minimum) => bs(l, mid-1, minimum) case mid if(p(mid) && mid < minimum) => bs(mid+1, h, mid) case _ => mid } } bs(l, h, 0) } l都是属于h的值的索引,而mid无法访问该值,但是它们本身不是价值。这与您要寻找最小值的陈述相矛盾;相反,您似乎正在寻找最小值的 index
  3. 在测试条件下,您期望值4为值7的索引。但是,7不能使谓词失效,因为它不少于5。这使我怀疑测试中的谓词功能应该是minimum
  4. 您没有验证Vector中的binarySearch小于binarySearch(v(_) >= 5)(0, v.length),这表明您没有搜索范围。如果在l中发生了这种情况,则应将其视为终止条件(已完全搜索范围),并返回找到的最小值索引。 (您现有的代码中似乎没有这样的终止条件,因此它可以在某些情况下无限循环,最终导致h。)
  5. 您应注意,带有谓词功能的二进制搜索从根本上来说是有缺陷的,除非谓词对范围进行了划分,以使所有未通过谓词的值的索引都是连续的,并且出现在范围的开头,并且所有通过谓词的值在范围的末尾都是连续的。为了说明这一点,请考虑如果谓词仅接受偶数:binarySearch,会发生什么? (提示:您的函数将需要访问范围内的每个元素以确保找到最小值,而这并不是二进制搜索的目的。)
  6. 如果您的搜索找不到满足该谓词的最小值,那么它将无法表示事实。因此,我认为您的函数应该返回bs,如果找不到值,则返回值StackOverflowError,否则返回binarySearch(v(_) % 2 == 0)(0, v.length)
  7. 如果为Option[Int]传递None,则如果检查具有该索引的元素的值,则会引发Some(minimum)。您应该改为传递v.length,或将h视为超出范围末端的一个位置。

更新

为了解决您的实施问题,我对问题进行了略微调整。现在,IndexOutOfBoundsException函数使用二进制搜索来查找大于指定最小值的最小值。为此,它接受名为v.length - 1的{​​{1}}和可以接受的h值,而不是具有低和高索引的谓词函数。

binarySearch

以下是 Scala REPL 中的一些示例:

IndexedSeq

答案 1 :(得分:0)

我的代码很复杂,需要使用辅助函数。.这可能是正确的解决方案:

def binarySearch: (Int => Boolean) => (Int, Int) => Int = f => (l, h) => {
val mid = l + ((h-l) / 2)
mid match {
  case _ if(l >= h) => h
  case mid if(f(mid)) => binarySearch(f)(l,mid)
  case mid => binarySearch(f)(mid+1, h)
}

}

不幸的是,我们必须使用这种声明方法的方式