Obtenga el índice del elemento de la matriz más rápido que O (n)

104

Dado que tengo una ENORME matriz y un valor de ella. Quiero obtener el índice del valor en la matriz. ¿Hay alguna otra forma, en lugar de llamar Array#indexpara conseguirlo? El problema proviene de la necesidad de mantener una matriz realmente enorme y llamar una Array#indexgran cantidad de veces.

Después de un par de intentos, descubrí que el almacenamiento en caché de los índices dentro de los elementos almacenando estructuras con (value, index)campos en lugar del valor en sí da un gran paso en el rendimiento (20 veces más ganado).

Aún así, me pregunto si hay una forma más conveniente de encontrar el índice de un elemento sin almacenamiento en caché (o existe una buena técnica de almacenamiento en caché que aumentará el rendimiento).

gmile
fuente

Respuestas:

118

Convierta la matriz en un hash. Luego busque la llave.

array = ['a', 'b', 'c']
hash = Hash[array.map.with_index.to_a]    # => {"a"=>0, "b"=>1, "c"=>2}
hash['b'] # => 1
sawa
fuente
2
más rápido si la matriz es muy larga
Kevin
17
Dependiendo de su caso de uso, esto podría ser problemático si hay valores duplicados. El método descrito anteriormente devolverá el equivalente o #rindex (última aparición de valor) Para obtener resultados equivalentes de #index, lo que significa que el hash devuelve el primer índice del valor que necesitaría hacer algo en la línea de invertir la matriz antes de crear luego, el hash resta el valor de índice devuelto de la longitud total de la matriz inicial - 1. # (matriz.length - 1) - hash ['b']
ashoda
2
¿No lleva O (n) tiempo la conversión a hash? Supongo que si se va a utilizar más de una vez, la conversión de hash será más eficaz. pero para un solo uso, ¿no es diferente a la iteración a través de la matriz?
ahnbizcad
Sí, y probablemente peor para un solo uso si realmente importa, ya que el cálculo de hash no provocará un cortocircuito tan rápido como una comparación.
Peter DeWeese
199

¿Por qué no usar index o 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')

índice: 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

Roger
fuente
13
Esto es exactamente lo que el OP dijo que NO querían, debido al gran tamaño de su matriz. Array # index es O (n) y hacerlo varias veces matará el rendimiento. La búsqueda de hash es O (1).
Tim
4
@tim, bueno, no puedo recordar en el momento de mi respuesta que ESTA era la misma pregunta, tal vez el OP revisó la pregunta más adelante, lo que invalidaría esta respuesta.
Roger
3
¿No diría entonces que se había editado en un momento específico?
Tim
Jeje, sí, eso es cierto. Bueno, yo y otras 30 personas lo estábamos leyendo. Supongo: /
Roger
9

Otras respuestas no tienen en cuenta la posibilidad de que una entrada aparezca varias veces en una matriz. Esto devolverá un hash donde cada clave es un objeto único en la matriz y cada valor es una matriz de índices que corresponde al lugar donde vive el objeto:

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

Esto permite una búsqueda rápida de entradas duplicadas:

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

¿Existe una buena razón para no usar un hash? Las búsquedas son O(1)vs. O(n)para la matriz.

Erik Peterson
fuente
El punto es que estoy llamando #keysa hash, que devuelve una matriz que estoy usando. Aún así, podría pensar en mi arquitectura también ...
gmile
3

Si es una matriz ordenada , puede usar un algoritmo de búsqueda binaria ( O(log n)). Por ejemplo, ampliando la clase Array con esta funcionalidad:

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
isakkarlsson
fuente
3
En realidad, no es tan difícil de leer. Primera parte, devuelva si el límite inferior es mayor que el límite superior (la recursividad se ha archivado). La segunda parte comprueba si necesitamos el lado izquierdo o el derecho comparando el punto medio m con el valor en ese punto ae. si no tenemos la respuesta que queremos, recurrimos.
ioquatix
Creo que es mejor para el ego de las personas que votan negativamente en lugar de editar.
Andre Figueiredo
2

Tomando una combinación de la respuesta de @ sawa y el comentario que aparece allí, podría implementar un índice "rápido" y un rindex en la clase de matriz.

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
ianstarz
fuente
2

Si su matriz tiene un orden natural, utilice la búsqueda binaria.

Utilice la búsqueda binaria.

La búsqueda binaria tiene O(log n)tiempo de acceso.

Estos son los pasos sobre cómo utilizar la búsqueda binaria,

  • ¿Cuál es el orden de su matriz? Por ejemplo, ¿está ordenado por nombre?
  • Úselo bsearchpara buscar elementos o índices

Ejemplo de código

# 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
akuhn
fuente
0

Aún así, me pregunto si hay una forma más conveniente de encontrar el índice de un elemento sin almacenamiento en caché (o existe una buena técnica de almacenamiento en caché que aumentará el rendimiento).

Puede usar la búsqueda binaria (si su matriz está ordenada y los valores que almacena en la matriz son comparables de alguna manera). Para que eso funcione, necesita poder decirle a la búsqueda binaria si debe mirar "a la izquierda" o "a la derecha" del elemento actual. Pero creo que no hay nada de malo en almacenar el indexen el momento de la inserción y luego usarlo si obtiene el elemento de la misma matriz.

Julik
fuente