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


104

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

数回試行した結果、値自体ではなくフィールドを持つ構造体を格納することで要素内にインデックスをキャッシュすると(value, index)、パフォーマンスが大幅に向上することがわかりました(20倍の勝率)。

それでも、キャッシュせずにen要素のインデックスを見つけるより便利な方法があるかどうか(または、パフォーマンスを向上させる優れたキャッシュテクニックがあるかどうか)。

回答:


118

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

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

2
アレイが非常に長い場合は最速
Kevin

17
ユースケースによっては、値が重複している場合に問題が発生する可能性があります。上記のメソッドは、同等の値または#rindex(値の最後の出現)を返します。#indexの同等の結果を取得するには、作成する前に配列を逆にする行に沿って何かを行う必要がある値の最初のインデックスを返すハッシュその後、ハッシュは返されたインデックス値を初期配列の全長から減算します-1。#(array.length-1)-hash ['b']
ashoda

2
ハッシュへの変換にはO(n)時間はかかりませんか?私はそれが2回以上使用されることになるとしたら、ハッシュ変換はよりパフォーマンスが高くなるでしょう。しかし、1回の使用では、配列を反復することと同じですか?
ahnbizcad 2016

はい、そしてハッシュ計算が比較ほど迅速に短絡しないので本当に重要な場合は、おそらくシングルユースではさらに悪いことです。
Peter DeWeese

199

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


13
アレイのサイズが大きいため、これはまさにOPがDIDNが望んでいないことだと言っていました。Array#indexはO(n)であり、それを複数回行うとパフォーマンスが低下します。ハッシュ検索はO(1)です。
ティム

4
@tim、まあ、私はこれが同じ質問だったことを私の回答の時点で思い出せません。おそらく、OPが後で質問を修正したため、この回答は無効になります。
ロジャー

3
その時、それが特定の時間に編集されたとは言わないでしょうか?
Tim

へへ、そうだね。さて、私と他の30人がそれを読みました。私は推測します:/
ロジャー

9

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

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] }

6

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


重要なのは#keys、私が使用している配列を返すハッシュを呼び出すことです。それでも、私は自分のアーキテクチャについても考えるかもしれません...
gmile

3

それはだ場合は、ソート配列を使用すると、バイナリサーチアルゴリズムを使用することができます(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
それは実際にはそれほど難しくありません。最初の部分。下限が上限より大きい場合(再帰が提出された場合)に戻ります。2番目の部分では、中点mとその点の値eを比較して、左側または右側が必要かどうかを確認します。欲しい答えがなければ、再帰します。
ioquatix 2014

編集よりも投票に反対する人の自我により良いと思います。
Andre Figueiredo

2

@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

配列に自然な順序がある場合は、バイナリ検索を使用してください。

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

バイナリ検索には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

0

それでも、キャッシュせずにen要素のインデックスを見つけるより便利な方法があるかどうか(または、パフォーマンスを向上させる優れたキャッシュテクニックがあるかどうか)。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.