107 votes

Obtenir l'index d'un élément de tableau plus rapidement que O(n)

Étant donné que j'ai un GRAND tableau, et une valeur de celui-ci. Je veux obtenir l'index de la valeur dans le tableau. Existe-t-il un autre moyen, plutôt que d'appeler Array#index pour l'obtenir ? Le problème vient de la nécessité de conserver un tableau vraiment énorme et d'appeler Array#index énormément de fois.

Après quelques essais, j'ai trouvé que mise en cache les index à l'intérieur des éléments en stockant les structs avec (value, index) au lieu de la valeur elle-même donne un énorme gain de performance (20x le gain).

Je me demande tout de même s'il n'y a pas un moyen plus pratique de trouver l'index d'un élément sans mise en cache (ou s'il existe une bonne technique de mise en cache qui améliore les performances).

199voto

Rogier Points 3237

Pourquoi ne pas utiliser index ou 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')

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

15 votes

C'est exactement ce que le PO a dit qu'il ne voulait pas, en raison de la grande taille de son tableau. Array#index est O(n) et le faire plusieurs fois va tuer les performances. La recherche dans le hachage est O(1).

4 votes

Je ne me souviens pas, au moment où j'ai répondu, que c'était la question que je voulais poser. même peut-être que le PO a révisé la question par la suite, ce qui invaliderait cette réponse.

3 votes

Ne dirait-on pas alors qu'il a été édité à un moment précis ?

121voto

sawa Points 62592

Convertir le tableau en hachage. Puis cherchez la clé.

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

3 votes

Plus rapide si le tableau est très long

18 votes

Selon votre cas d'utilisation, cela pourrait être problématique s'il y a des valeurs en double. La méthode décrite ci-dessus retournera l'équivalent ou #rindex(dernière occurrence de la valeur) Pour obtenir des résultats équivalents à #index, c'est-à-dire le hachage retournant le premier indice de la valeur, vous devrez faire quelque chose du genre inverser le tableau avant de créer le hachage puis soustraire la valeur de l'indice retourné de la longueur totale du tableau initial - 1. # (array.length - 1 ) - hash['b']

3 votes

La conversion en hachage ne prend-elle pas un temps O(n) ? Je suppose que si le tableau est utilisé plusieurs fois, la conversion en hash sera plus performante. Mais pour un usage unique, n'est-ce pas différent que d'itérer dans le tableau ?

11voto

hololeap Points 169

Les autres réponses ne prennent pas en compte la possibilité qu'une entrée soit listée plusieurs fois dans un tableau. Cette réponse renverra un hachage où chaque clé est un objet unique dans le tableau et chaque valeur est un tableau d'indices qui correspond à l'emplacement de l'objet :

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

Cela permet une recherche rapide des doublons :

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

0 votes

Cette réponse est plus complète que celle de @sawa.

6voto

Erik Peterson Points 1768

Y a-t-il une bonne raison de ne pas utiliser un hachage ? Les recherches sont O(1) vs. O(n) pour le tableau.

0 votes

Le fait est que j'appelle #keys sur hash, qui renvoie un tableau que j'utilise. Néanmoins, je pourrais aussi réfléchir à mon architecture...

3voto

isakkarlsson Points 551

Si c'est un trié vous pouvez utiliser un algorithme de recherche binaire ( O(log n) ). Par exemple, l'extension de la classe Array avec cette fonctionnalité :

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 votes

En fait, ce n'est pas si difficile à lire. La première partie retourne si la limite inférieure est plus grande que la limite supérieure (la récursion est terminée). La seconde partie vérifie si nous avons besoin du côté gauche ou du côté droit en comparant le point médian m avec la valeur à ce point de e. Si nous n'avons pas la réponse que nous voulons, nous récurons.

0 votes

Je pense qu'il fait mieux à l'ego des gens de downvoter plutôt que d'éditer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X