Économies lors de l'initialisation de la baie

19

J'ai récemment lu qu'il est possible d'avoir des tableaux qui n'ont pas besoin d'être initialisés, c'est-à-dire qu'il est possible de les utiliser sans avoir à passer du temps à essayer de définir chaque membre à la valeur par défaut. c'est-à-dire que vous pouvez commencer à utiliser le tableau comme s'il avait été initialisé par la valeur par défaut sans avoir à l'initialiser. (Désolé, je ne me souviens pas où j'ai lu ceci).

Par exemple, pourquoi cela peut être surprenant:

Dites que vous essayez de modéliser un pire cas Hashtable (pour chacun d' insertion / suppression / recherche) des entiers dans l'intervalle .[ 1 , n 2 ]O(1)[1,n2]

Vous pouvez allouer un tableau de taille bits et utiliser des bits individuels pour représenter l'existence d'un entier dans la table de hachage. Remarque: l'allocation de mémoire est considérée comme du temps .O ( 1 )n2O(1)

Maintenant, si vous n'aviez pas du tout à initialiser ce tableau, toute séquence d' opérations dites sur cette table de hachage est maintenant le pire des cas .O ( n )nO(n)

Donc en fait, vous avez une implémentation de hachage "parfaite", qui pour une séquence de opérations utilise l' espace , mais s'exécute en temps !Θ ( n 2 ) O ( n )nΘ(n2)O(n)

Normalement, on s'attendrait à ce que votre temps d'exécution soit au moins aussi mauvais que votre utilisation de l'espace!

Remarque: L'exemple ci-dessus peut être utilisé pour une implémentation d'un ensemble clairsemé ou d'une matrice clairsemée, donc ce n'est pas seulement d'intérêt théorique, je suppose.

La question est donc:

Comment est-il possible d'avoir une structure de données de type tableau qui nous permet de sauter l'étape d'initialisation?

Aryabhata
la source
@Aryabhata Quelle est la référence que vous avez mentionnée?
uli
1
«utiliser la mémoire» n'est pas la même chose que «avoir alloué mais jamais accédé à la mémoire», donc je pense que le «paradoxe» motivant n'existe pas tout à fait.
Raphael
1
Je pense que le premier paragraphe est assez clair: avoir une valeur par défaut, sans vraiment prendre le temps de remplir le tableau avec la valeur par défaut. La réponse, au cas où quelqu'un d'autre aurait le temps de l'écrire avant moi, est ici scholar.google.co.uk/… Il y a aussi une très brève explication sur mon blog rgrig.blogspot.co.uk/2008/12/array -puzzle-solution.html
rgrig
@uli: C'est une question d'amorçage, je l'ai lu il y a longtemps .
Aryabhata
@Raphael: C'est toujours surprenant quand vous entendez parler d'une telle chose la première fois. La plupart des paradoxes ne le sont pas :-)
Aryabhata

Réponses:

15

Il s'agit d'une astuce très générale, qui peut être utilisée à d'autres fins que le hachage. Ci-dessous je donne une implémentation (en pseudo-code).

Soit trois vecteurs , P et V non initialisés de taille n chacun. Nous les utiliserons pour effectuer les opérations demandées par notre structure de données. Nous maintenons également une variable p o s . Les opérations sont mises en œuvre comme suit:APVnpos

init:
  pos <- 0

set(i,x):
if not(V[i] < pos and P[V[i]] = i) 
  V[i] <- pos, P[pos] <- i, pos <- pos + 1
A[i] <- x

get(i):
if (V[i] < pos and P[V[i]] = i) 
  return A[i] 
else 
  return empty 

Le tableau stocke simplement les valeurs transmises par la procédure s e t . Les tableaux V et P fonctionnent comme des certificats qui peuvent indiquer si une position donnée dans A a été initialisée.AsetVPA

Notez qu'à chaque instant les éléments de allant de 0 à p o s - 1 sont initialisés. On peut donc utiliser en toute sécurité ces valeurs comme un certificat pour les valeurs initialisées en A . Pour chaque position i dans A initialisée, il y a un élément correspondant dans le vecteur P dont la valeur est égale à i . Ceci est indiqué par V [ i ] . Par conséquent, si nous regardons l'élément correspondant, P [ V [ i ] ] et sa valeur est iP0pos1AiAPiV[i]P[V[i]]i, nous savons que a été initialisé (puisque P ne ment jamais, car tous les éléments que nous considérons sont initialisés). De même, si A [ i ] n'est pas initialisé, alors V [ i ] peut pointer soit vers une position dans P en dehors de la plage 0 .. p o s - 1 , quand nous savons avec certitude qu'il n'est pas initialisé, ou peut pointer vers une position dans cette plage. Mais ce P [ j ] particulier correspond à une position différente dans A , et doncA[i]PA[i]V[i]P0..pos1P[j]A , nous savons donc que A [ i ] n'a pas été initialisé.P[j]iA[i]

Il est facile de voir que toutes ces opérations se font en temps constant. De plus, l'espace utilisé est pour chacun des vecteurs, et O ( 1 ) pour la variable p o s , donc O ( n ) au total.O(n)O(1)posO(n)

zotachidil
la source
P[V[i]]iA[i]
C'est mais alors pos sera plus petit que V [i] maintenant non? Sinon, ce ne serait pas par hasard. Puisqu'en ayant pos supérieur à V [i], cela signifie que nous avons spécifiquement défini la valeur de P à l'indice V [i] sur une valeur spécifique que nous avons choisie, à savoir i.
wolfdawn
Notez que ceci est un exemple classique de quelque chose qui est impossible à faire en (portable) C.
TLW