Ruby 使用bsearch查找将新元素插入排序数组的索引

Ruby 使用bsearch查找将新元素插入排序数组的索引,ruby,bsearch,Ruby,Bsearch,我有一个已排序的唯一数组,希望有效地将数组中不存在的元素插入其中,如下所示: a = [1,2,4,5,6] new_elm = 3 insert_at = a.bsearch_index {|x| x > new_elm } # => 2 a.insert(insert_at, new_elm) # now a = [1,2,3,4,5,6] 方法bsearch\u index不存在:仅bsearch,它返回匹配元素而不是匹配元素的索引。有什么内置的方法可以实现这一点吗?使用如何

我有一个已排序的唯一数组,希望有效地将数组中不存在的元素插入其中,如下所示:

a = [1,2,4,5,6]
new_elm = 3
insert_at = a.bsearch_index {|x| x > new_elm } # => 2
a.insert(insert_at, new_elm) # now a = [1,2,3,4,5,6]

方法
bsearch\u index
不存在:仅
bsearch
,它返回匹配元素而不是匹配元素的索引。有什么内置的方法可以实现这一点吗?

使用如何

结果如下:

test_sorted 10_000
# =>       user     system      total        real
# =>   0.000000   0.000000   0.000000 (  0.000900)
# =>   0.010000   0.000000   0.010000 (  0.001868)
# =>   0.000000   0.000000   0.000000 (  0.000007)

test_sorted 100_000
# =>       user     system      total        real
# =>   0.000000   0.000000   0.000000 (  0.001150)
# =>   0.000000   0.010000   0.010000 (  0.048040)
# =>   0.000000   0.000000   0.000000 (  0.000013)

test_sorted 1_000_000
# =>       user     system      total        real
# =>   0.040000   0.000000   0.040000 (  0.062719)
# =>   0.280000   0.000000   0.280000 (  0.356032)
# =>   0.000000   0.000000   0.000000 (  0.000012)

index
方法接受一个块,并返回块为真的第一个索引

a = [1,2,4,5,6] 
new_elem = 3
insert_at = a.index{|b| b > new_elem}
#=> 2
a.insert(insert_at, new_elm) 
#=>[1,2,3,4,5,6]

您可以使用
枚举器
对象,该对象由
带有索引的每个\u返回,以返回
[值,索引]
对的嵌套数组,然后对该数组执行二进制搜索:

a = [1,2,4,5,6]
new_elm = 3

index = [*a.each_with_index].bsearch{|x, _| x > new_elm}.last
=> 2

a.insert(index, new_elm)
编辑:

为了回答您的问题,我运行了一些简单的基准测试,使用长度数组
1e6-1

require 'benchmark'

def binary_insert(a,e)
  index = [*a.each_with_index].bsearch{|x, _| x > e}.last
  a.insert(index, e)
end

a = *1..1e6
b = a.delete_at(1e5)
=> 100001

Benchmark.measure{binary_insert(a,b)}
=> #<Benchmark::Tms:0x007fd3883133d8 @label="", @real=0.37332, @cstime=0.0, @cutime=0.0, @stime=0.029999999999999805, @utime=0.240000000000002, @total=0.2700000000000018> 
需要“基准测试”
def二进制_插入(a、e)
index=[*a.each_with_index].b搜索{x,{x>e}.last
a、 插入(索引,e)
结束
a=*1..1e6
b=a.在(1e5)处删除
=> 100001
Benchmark.measure{binary_insert(a,b)}
=> # 

考虑到这一点,您可能会考虑尝试使用堆或TIE而不是数组来存储值。特别是堆具有恒定的插入和删除时间复杂性,非常适合于大型存储应用程序。在这里查看这篇文章:

“方法
bsearch\u index
不存在”:Ruby 2.3介绍。(在方法名称出现之前就获得了它,这是值得称赞的)

试试这个

(0...a.size).bsearch { |n| a[n] > new_element }
这将使用
Range
上定义的
b搜索
来搜索数组,从而返回索引


性能将远远优于
每个带有_索引的
,它具体化了
O(n)
临时数组元组,从而阻塞了垃圾收集。

引入了Ruby 2.3.1,因此现在可以通过以下方式解决问题:

a = [1,2,4,5,6]
new_elm = 3

index = a.bsearch_index{|x, _| x > new_elm}
=> 2

a.insert(index, new_elm)

index
还会一个接一个地搜索元素吗?@ArthurChamz-当然,你是对的,另一个人建议道avenue@UriAgassi,SortedSet是否在内部使用二进制搜索?我在docs@Jonah中找不到任何信息-SortedSet是建立在rbtree上的-谢谢Uri,这很有帮助。不幸的是,我已经超过了你:)可能是重复的等等,是你!是同一个问题还是我理解错了?是我!除非你仔细阅读这两个问题,否则你会发现它们根本不一样。这并不能回答这个问题,因为索引在内部不使用二进制搜索——这是问题的关键。@jonah为什么二进制搜索你没有对此进行说明,所以我根据我特别要求的示例给出了一个完全可行的选项b搜索,但返回索引。这就是问题所在。我特别提到效率。你所做的只是以我为例,用
索引
代替
b搜索索引
。显然,我知道
index
方法存在,所以很抱歉,您的答案不是“完全可行”的选项。@jonah就您的示例而言,就效率而言,它是可行的,您甚至测试过它。在B研究中,它远比链锁有效。看看乌里·阿加西的基准测试。排序集似乎是最有效的方法,但这是可行的,因为你认为二进制更有效并不能使它成为现实。有趣的想法。你知道演出会怎么样吗?我担心对于大型数组,枚举数到数组的转换会很慢,特别是在循环中使用它时,但我只是猜测它在内部是如何工作的。。。。
(0...a.size).bsearch { |n| a[n] > new_element }
a = [1,2,4,5,6]
new_elm = 3

index = a.bsearch_index{|x, _| x > new_elm}
=> 2

a.insert(index, new_elm)