web-dev-qa-db-ja.com

O(n)よりも速く配列要素のインデックスを取得します

巨大な配列と、そこからの値があります。配列の値のインデックスを取得したい。他の方法がありますが、Array#indexを呼び出して取得しますか?問題は、本当に巨大な配列を保持し、Array#indexを膨大な回数呼び出す必要があるためです。

数回の試行の後、値自体の代わりに(value, index)フィールドを持つ構造体を格納することでcaching要素内のインデックスを作成することで、パフォーマンスが大幅に向上します(20倍の勝利)。

それでも、キャッシュせずにen要素のインデックスを検索するより便利な方法があるのだろうか(または、パフォーマンスを向上させる優れたキャッシュ技術があるのか​​)。

102
gmile

配列をハッシュに変換します。次に、キーを探します。

array = ['a', 'b', 'c']
hash = Hash[array.map.with_index.to_a]    # => {"a"=>0, "b"=>1, "c"=>2}
hash['b'] # => 1
117
sawa

なぜindexまたはrindexを使用しないのですか?

array = %w( a b c d e)
# get FIRST index of element searched
puts array.index('a')
# get LAST index of element searched
puts array.rindex('a')

インデックス: http://www.Ruby-doc.org/core-1.9.3/Array.html#method-i-index

rindex: http://www.Ruby-doc.org/core-1.9.3/Array.html#method-i-rindex

199
Roger

他の回答では、エントリが配列に複数回リストされる可能性を考慮していません。これは、各キーが配列内の一意のオブジェクトであり、各値がオブジェクトの存在場所に対応するインデックスの配列であるハッシュを返します。

a = [1, 2, 3, 1, 2, 3, 4]
=> [1, 2, 3, 1, 2, 3, 4]

indices = a.each_with_index.inject(Hash.new { Array.new }) do |hash, (obj, i)| 
    hash[obj] += [i]
    hash
end
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5], 4 => [6] }

これにより、重複したエントリをすばやく検索できます。

indices.select { |k, v| v.size > 1 }
=> { 1 => [0, 3], 2 => [1, 4], 3 => [2, 5] }
9
hololeap

ハッシュを使用しない正当な理由はありますか?ルックアップは、配列のO(1)O(n)です。

6
Erik Peterson

ソート済み配列の場合、バイナリ検索アルゴリズム(O(log n))を使用できます。たとえば、次の機能を使用してArrayクラスを拡張します。

class Array
  def b_search(e, l = 0, u = length - 1)
    return if lower_index > upper_index

    midpoint_index = (lower_index + upper_index) / 2
    return midpoint_index if self[midpoint_index] == value

    if value < self[midpoint_index]
      b_search(value, lower_index, upper_index - 1)
    else
      b_search(value, lower_index + 1, upper_index)
    end
  end
end
3
isakkarlsson

@sawaの回答とそこにリストされているコメントを組み合わせて、配列クラスに「クイック」インデックスとrindexを実装できます。

class Array
  def quick_index el
    hash = Hash[self.map.with_index.to_a]
    hash[el]
  end

  def quick_rindex el
    hash = Hash[self.reverse.map.with_index.to_a]
    array.length - 1 - hash[el]
  end
end
2
ianstarz

配列に自然順序がある場合は、バイナリ検索を使用します。

バイナリ検索を使用します。

バイナリ検索にはO(log n)アクセス時間があります。

バイナリ検索の使用方法の手順は次のとおりです。

  • あなたの配列の順序は何ですか?たとえば、名前でソートされていますか?
  • bsearchを使用して、要素またはインデックスを検索します

コード例

# assume array is sorted by name!

array.bsearch { |each| "Jamie" <=> each.name } # returns element
(0..array.size).bsearch { |n| "Jamie" <=> array[n].name } # returns index
2
akuhn

それでも、キャッシュせずにen要素のインデックスを検索するより便利な方法があるのだろうか(または、パフォーマンスを向上させる優れたキャッシュ技術があるのか​​)。

バイナリ検索を使用できます(配列が順序付けられている場合and配列に格納する値は何らかの方法で比較可能です)。それが機能するためには、現在の要素の「左」または「右」のどちらに見えるべきかをバイナリ検索に伝えることができる必要があります。しかし、挿入時にindexを保存し、同じ配列から要素を取得している場合はそれを使用しても問題はないと思います。

0
Julik