it-swarm.com.de

Index des Array-Elements schneller abrufen als O(n)

Dazu habe ich ein RIESIGES Array und einen Wert daraus. Ich möchte den Index des Wertes im Array erhalten. Gibt es eine andere Möglichkeit, als Array#index Aufzurufen, um es zu bekommen? Das Problem ergibt sich aus der Notwendigkeit, ein wirklich großes Array zu behalten und Array#index Unzählige Male aufzurufen.

Nach ein paar Versuchen stellte ich fest, dass Caching durch Speichern von Strukturen mit (value, index) - Feldern anstelle des Werts selbst innerhalb von Elementen einen enormen Leistungssprung ergibt (20-facher Gewinn).

Ich frage mich immer noch, ob es einen bequemeren Weg gibt, den Index eines Elements ohne Zwischenspeicherung zu finden (oder ob es eine gute Zwischenspeichertechnik gibt, die die Leistung steigert).

102
gmile

Konvertieren Sie das Array in einen Hash. Dann suchen Sie nach dem Schlüssel.

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

Warum nicht index oder rindex verwenden?

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')

index: 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

Andere Antworten berücksichtigen nicht die Möglichkeit, dass ein Eintrag mehrmals in einem Array aufgeführt wird. Dies gibt einen Hash zurück, bei dem jeder Schlüssel ein eindeutiges Objekt im Array ist und jeder Wert ein Array von Indizes ist, das dem Ort entspricht, an dem sich das Objekt befindet:

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

Dies ermöglicht eine schnelle Suche nach doppelten Einträgen:

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

Gibt es einen guten Grund, keinen Hash zu verwenden? Lookups sind O(1) vs. O(n) für das Array.

6
Erik Peterson

Wenn es sich um ein sortiert Array handelt, können Sie einen binären Suchalgorithmus (O(log n)) verwenden. Beispiel: Erweitern der Array-Klasse mit dieser Funktionalität:

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

Wenn Sie die Antwort von @ sawa und den dort aufgeführten Kommentar kombinieren, können Sie einen "schnellen" Index und einen rindex für die Array-Klasse implementieren.

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

Wenn Ihr Array eine natürliche Reihenfolge hat, verwenden Sie die binäre Suche.

Verwenden Sie die binäre Suche.

Die binäre Suche hat O(log n) Zugriffszeit.

Hier sind die Schritte zur Verwendung der binären Suche:

  • Wie ist die Reihenfolge Ihres Arrays? Ist es zum Beispiel nach Namen sortiert?
  • Verwenden Sie bsearch, um nach Elementen oder Indizes zu suchen

Codebeispiel

# 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

Ich frage mich immer noch, ob es einen bequemeren Weg gibt, den Index eines Elements ohne Zwischenspeicherung zu finden (oder ob es eine gute Zwischenspeichertechnik gibt, die die Leistung steigert).

Sie können die binäre Suche verwenden (wenn Ihr Array geordnet ist nd die Werte, die Sie im Array speichern, sind in gewisser Weise vergleichbar). Damit dies funktioniert, müssen Sie in der Lage sein, der binären Suche mitzuteilen, ob sie "nach links" oder "nach rechts" des aktuellen Elements schauen soll. Aber ich glaube, es ist nichts Falsches daran, das index beim Einfügen zu speichern und es dann zu verwenden, wenn Sie das Element aus demselben Array erhalten.

0
Julik