我正在阅读the docs for Ruby's bsearch。
似乎当该块返回true或false时,然后bsearch使用“ find-minimum”模式进行工作。还有最大查找模式吗?
对于以下第3至5种情况,我不太了解Ruby的bsearch最小查找行为:
[10, 20, 50, 80, 110].bsearch{|a| a >= 25}
=> 50
[10, 20, 50, 80, 110].bsearch{|a| a >= 20}
=> 20
[10, 20, 50, 80, 110].bsearch{|a| a == 20}
=> nil
[10, 20, 50, 80, 110].bsearch{|a| a < 50}
=> nil
[10, 20, 50, 80, 110].bsearch{|a| a <= 50}
=> 10
20
?20
? (第一个小于50)。10
而不是20
?此外,当块不返回true或false而是返回数字时,bsearch似乎将使用find-any
模式。但是我真的无法理解它在文档中的作用:
ary = [0, 4, 7, 10, 12]
# try to find v such that 4 <= v < 8
ary.bsearch {|x| 1 - x / 4 } #=> 4 or 7
例如1 - x / 4
是什么,它在做什么?
答案 0 :(得分:6)
- 对于第三种情况,为什么找不到
20
?- 对于第四种情况,为什么也找不到
20
? (第一个小于50)。- 对于第五种情况,为什么找到
10
而不是20
?
您的示例3、4和5违反了该方法的前提条件,因此该方法可以执行所需的任何操作,包括返回nil
,返回10
或格式化硬盘。 (尽管最后一个可能性很小。)
该块必须返回true或false,并且必须有一个索引i(0 <= i <= ary.size),以便:
- 对于索引小于i的任何元素,该块返回false,并且
- 对于索引大于或等于i的任何元素,该块返回true。
您的代码块违反了该条件,因此方法不可能返回有意义的结果。
对于第三种情况,让我们实际完成Array#bsearch
的示例运行:
[10, 20, 50, 80, 110].bsearch{|a| a == 20 }
在第一次迭代中,算法将选择数组的中间(50
)并针对您的块进行测试:
50 == 20 #=> false
值false
表示我们正在搜索的元素位于当前元素的 right 处。如此,我们要搜索的元素必须在子数组[80, 110]
中。因此,我们递归:
[80, 110].bsearch{|a| a == 20 }
同样,我们选择“中间”元素(110
)并针对您的代码块进行测试:
110 == 20 #=> false
由于该块的返回值为false
,因此我们知道该元素必须在当前元素的右边,,但是右边没有元素,因此我们知道我们要搜索的元素不存在。
现在是您的第四种情况:
[10, 20, 50, 80, 110].bsearch{|a| a < 50 }
在第一次迭代中,算法将选择数组的中间(50
)并针对您的块进行测试:
50 < 50 #=> false
值false
表示我们正在搜索的元素位于当前元素的 right 处。如此,我们要搜索的元素必须在子数组[80, 110]
中。因此,我们递归:
[80, 110].bsearch{|a| a < 50 }
同样,我们选择“中间”元素(110
)并针对您的代码块进行测试:
110 < 50 #=> false
由于该块的返回值为false
,因此我们知道该元素必须在当前元素的右边,,但是右边没有元素,因此我们知道我们要搜索的元素不存在。
第五种情况:
[10, 20, 50, 80, 110].bsearch{|a| a <= 50 }
在第一次迭代中,算法将选择数组的中间(50
)并针对您的块进行测试:
50 <= 50 #=> true
值为true
表示我们正在搜索的元素位于当前元素的 left 处。如此,我们要搜索的元素必须在子数组[10, 20]
中。因此,我们递归:
[10, 20].bsearch{|a| a <= 50 }
同样,我们选择“中间”元素(20
)并针对您的代码块进行测试:
20 <= 50 #=> true
因此,该元素必须仍位于左侧子数组中:
[10].bsearch{|a| a <= 50 }
将测试
10 <= 50 #=> true
由于该块的返回值为true
,因此我们知道该元素必须位于当前元素或此元素的左侧,但是没有元素可以是正确的,因此,我们知道我们要搜索的元素就是这个元素。
注意:我假定,Array#bsearch
总是会选择一个尽可能靠近中间的元素,并且我假设对于偶数个元素,它总是会选择中间的一个。但是您知道他们对假设的看法:它使您和我陷入困境。实际上,文档明确指出:
每次迭代中实际获取哪个值是不确定的。
因此,根据哪些实际在每次迭代中实际拾取的元素,结果实际上可能有所不同。而且这也不足为奇,因为该块违反了该方法的前提,所以没有办法知道将要发生什么。
例如
1 - x / 4
是什么,它在做什么?
这只是您的搜索条件。 0
是x == 4
,x < 4
是正,x > 4
是负。这正是 find-any 模式所需要的:正数告诉算法它必须向左看,负数告诉算法必须向右看,零表示“您找到了范围”:
该块必须始终返回一个数字,并且必须有两个索引i和j(0 <= i <= j <= ary.size),以便:
- 如果0 <= k 该块为ary返回一个正数
- 如果i <= k
- 如果j <= k
答案 1 :(得分:2)
在您的第三个示例中,您没有满足“查找最小”模式的要求之一,即该块返回true
的任何值大于或等于您的搜索值。
在第三种情况下,您已经切换了值,因此对于小值返回true
,对于大值返回false
,这再次导致未定义的行为。
类似地,在第一种情况下,您已切换了相等性检查的顺序。该方法的要求是,如果搜索值等于或大于搜索值,则该块返回true
;如果小于或等于false
,则该块返回。
通常,如果您可以满足bsearch算法的所有要求,即对搜索到的数组进行排序并指定合适的块,则bsearch算法非常有效。在这种情况下,使用基本的find
方法可能会更有效率。
但是,如果您缺少其任何要求,您将获得不确定的行为并有效地获得随机结果。在这种情况下,仅使用Enumerable#find
可能也会有更好的结果,该方法同样适用于数组,哈希,...
答案 2 :(得分:1)
尽管有文档,Array#bsearch
没有
从此数组中找到一个满足给定条件的值
该方法的确切作用是要求您构造这样的数组ary
和这样的块blk
,
...代码ary.map(&blk)
应该返回一个类似的数组
[false, false, ..., false, false, true, true, ..., true, true]
# ^^^^
然后代码ary.bsearch(&blk)
将返回给您最左侧数组元素,该元素将为true
返回bkl.call(element)
。
...代码ary.map(&blk)
应该返回一个类似的数组
[positive, ..., positive, 0, ..., 0, negative, ..., negative]
# ^^^^^^^^^
然后代码ary.bsearch(&blk)
将返回给您 first-met-by-bisect-jump 数组元素,该数组元素为0
返回bkl.call(element)
。
[10, 20, 50, 80, 110].bsearch{|a| a == 20}
毫无意义
[10, 20, 50, 80, 110].map{|a| a == 20}
=> [false, true, false, false, false]
可以更改
[10, 20, 50, 80, 110].bsearch{|a| a < 50}
以满足最小查找模式的要求:
[10, 20, 50, 80, 110].reverse.map{|a| a < 50}
=> [false, false, false, true, true]
[10, 20, 50, 80, 110].reverse.bsearch{|a| a < 50}
=> 20
[10, 20, 50, 80, 110].bsearch{|a| a <= 50}
也可以使用reverse
:
[10, 20, 50, 80, 110].reverse.bsearch{|a| a <= 50}
=> 50