【问题标题】:Using bsearch to find index for inserting new element into sorted array使用 bsearch 查找将新元素插入排序数组的索引
【发布时间】:2014-06-22 07:13:25
【问题描述】:

我有一个排序的唯一数组,并希望有效地将一个不在数组中的元素插入其中:

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_index不存在:只有bsearch,它返回匹配元素而不是匹配元素的索引。有没有内置的方法可以做到这一点?

【问题讨论】:

  • 等一下,是你!是同一个问题还是我理解错了?
  • 是我!除非您仔细阅读这两个问题,否则您会发现它们根本不一样。

标签: ruby bsearch


【解决方案1】:

您可以使用each_with_index 返回的Enumerator 对象返回[value, index] 对的嵌套数组,然后对该数组进行二进制搜索:

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> 

考虑到这一点,您可能会考虑尝试使用堆或树而不是数组来存储您的值。特别是堆具有恒定的插入和删除时间复杂性,使其成为大型存储应用程序的理想选择。在这里查看这篇文章:Ruby algorithms: sorting, trie, and heaps

【讨论】:

  • 有趣的想法。你知道表演会如何吗?我担心 Enumerator 到数组的转换对于大型数组来说会很慢,特别是如果我在循环中使用它,但我只是猜测它在内部是如何工作的......
【解决方案2】:

Ruby 2.3.1 引入了bsearch_index,因此问题现在可以这样解决:

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

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

a.insert(index, new_elm)

【讨论】:

    【解决方案3】:

    试试这个

    (0...a.size).bsearch { |n| a[n] > new_element }
    

    这使用在Range 上定义的bsearch 来搜索数组并因此返回索引。


    性能将比 each_with_index 好得多,后者实现了 O(n) 临时数组元组,从而阻塞了垃圾收集。

    【讨论】:

      【解决方案4】:

      bsearch_index 方法不存在”:Ruby 2.3 引入了bsearch_index。 (在它存在之前获得方法名称的荣誉)。

      【讨论】:

        【解决方案5】:

        使用SortedSet怎么样?:

        require 'set'
        
        a = SortedSet.new [1,2,4,5,6]
        new_elm = 3
        a << new_elm # now a = #<SortedSet: {1, 2, 3, 4, 5, 6}>
        

        SortedSet 是使用rbtree 实现的。我做了以下基准测试:

        def test_sorted(max_idx)
          arr_1 = (0..max_idx).to_a
          new_elm = arr_1.delete(arr_1.sample)
          arr_2 = arr_1.dup
          set_1 = SortedSet.new(arr_1)
          Benchmark.bm do |x|
            x.report { arr_1.insert(arr_1.index { |x| x > new_elm }) }
            x.report { arr_2.insert([*arr_2.each_with_index].bsearch{|x, _| x > new_elm}.last) }
            x.report { set_1 << new_elm }
          end
        end
        

        结果如下:

        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)
        

        【讨论】:

        【解决方案6】:

        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]
        

        【讨论】:

        • 这并不能回答问题,因为索引内部不使用二进制搜索——这是问题的关键。
        • @jonah 为什么二进制搜索您对此没有任何理由,所以我根据示例给出了一个完全可行的选项
        • 我特别要求 bsearch,但返回一个索引。那的问题。我特别提到效率。你所做的只是以我为例,用index 代替bsearch_index。显然我知道 index 方法存在,很抱歉,您的回答不是“完全可行”的选项。
        • @jonah 以您的示例为例,就效率而言,您甚至对其进行了测试都是可行的。它比 bsearch 中的链接效率高得多。查看 Uri Agassi 的基准。 Sorted Set 似乎是最有效的方法,但这是可行的,因为您认为二进制更有效并不能实现。
        猜你喜欢
        • 2020-09-06
        • 1970-01-01
        • 2017-12-14
        • 2016-06-08
        • 2014-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多