web-dev-qa-db-fra.com

Complexité temporelle de l'accès à un Python dict

J'écris un simple programme Python.

Mon programme semble souffrir d'un accès linéaire aux dictionnaires, son temps d'exécution croît de façon exponentielle même si l'algorithme est quadratique.
J'utilise un dictionnaire pour mémoriser les valeurs. Cela semble être un goulot d'étranglement.

Les valeurs que je hache sont des tuples de points. Chaque point est: (x, y), 0 <= x, y <= 50
Chaque clé du dictionnaire est: Un tuple de 2 à 5 points: ((x1, y1), (x2, y2), (x3, y3), (x4, y4))

Les clés sont lues plusieurs fois plus souvent qu'elles ne sont écrites.

Ai-je raison de dire que python les dict souffrent de temps d'accès linéaires avec de telles entrées?

Pour autant que je sache, les ensembles ont des temps d'accès logarithmiques garantis.
Comment puis-je simuler des dict en utilisant des ensembles (ou quelque chose de similaire) en Python?

edit Selon la demande, voici une version (simplifiée) de la fonction de mémorisation:

def memoize(fun):
    memoized = {}
    def memo(*args):
        key = args
        if not key in memoized:
            memoized[key] = fun(*args)
        return memoized[key]
    return memo
36
x10

Voir Complexité temporelle . Le dict python est une table de hachage, son pire cas est donc O(n) si la fonction de hachage est mauvaise et entraîne de nombreuses collisions. Cependant, cela est un cas très rare où chaque élément ajouté a le même hachage et est donc ajouté à la même chaîne qui, pour une implémentation majeure Python serait extrêmement peu probable La complexité moyenne en temps est bien sûr O (1).

La meilleure méthode serait de vérifier et d'examiner les hachages des objets que vous utilisez. CPython Dict utilise int PyObject_Hash (PyObject * o) qui est l'équivalent de hash(o).

Après une vérification rapide, je n'ai pas encore réussi à trouver deux tuples qui hachent à la même valeur, ce qui indiquerait que la recherche est O (1)

l = []
for x in range(0, 50):
    for y in range(0, 50):
        if hash((x,y)) in l:
            print "Fail: ", (x,y)
        l.append(hash((x,y)))
print "Test Finished"

CodePad (Disponible pendant 24 heures)

58
Yacoby

Tu n'as pas raison. dict l'accès ne sera probablement pas votre problème ici. C'est presque certainement O (1), sauf si vous avez des entrées très étranges ou une très mauvaise fonction de hachage. Collez un exemple de code de votre application pour un meilleur diagnostic.

4
Eli Bendersky

Il serait plus facile de faire des suggestions si vous fournissez un exemple de code et de données.

Il est peu probable que l'accès au dictionnaire soit un problème car cette opération est O (1) en moyenne et O(N) amorti dans le pire des cas . Il est possible que le -les fonctions de hachage rencontrent des collisions pour vos données. Si vous rencontrez des problèmes avec la fonction de hachage intégrée, vous pouvez fournir la vôtre.

L'implémentation de dictionnaire de Python réduit la complexité moyenne des recherches de dictionnaire à O(1) en exigeant que les objets clés fournissent une fonction de "hachage". Une telle fonction de hachage prend les informations dans un objet clé et l'utilise pour produire un entier, appelé valeur de hachage. Cette valeur de hachage est ensuite utilisée pour déterminer dans quel "compartiment" cette paire (clé, valeur) doit être placée.

Vous pouvez remplacer la méthode __hash__ dans votre classe pour implémenter une fonction de hachage personnalisée comme ceci:

def __hash__(self):    
    return hash(str(self))

Selon l'apparence réelle de vos données, vous pourrez peut-être proposer une fonction de hachage plus rapide qui a moins de collisions que la fonction standard. Cependant, cela est peu probable. Voir page Wiki Python sur les touches du dictionnaire pour plus d'informations.

3
James Thompson

Pour répondre à vos questions spécifiques:

Q1: "" "Ai-je raison de dire que python dict souffrent de temps d'accès linéaires avec de telles entrées?" ""

A1: Si vous voulez dire que le temps de recherche moyen est O(N) où N est le nombre d'entrées dans le dict, alors il est fort probable que vous vous trompiez. Si vous avez raison, le Python aimerait beaucoup savoir dans quelles circonstances vous avez raison, afin que le problème puisse être atténué ou au moins prévenu. Ni le code "exemple" ni le code "simplifié" ne sont utiles. Veuillez afficher le code réel et les données qui reproduisent le problème. Le code doit être instrumenté avec des éléments comme le nombre d'éléments de dict et le nombre d'accès au dict pour chaque P où P est le nombre de points dans la clé (2 <= P <= 5)

Q2: "" "Pour autant que je sache, les ensembles ont des temps d'accès logarithmiques garantis. Comment puis-je simuler des dict en utilisant des ensembles (ou quelque chose de similaire) en Python?" ""

A2: Les ensembles ont des temps d'accès logarithmiques garantis dans quel contexte? Il n'y a pas de telle garantie pour les implémentations Python. Les versions récentes de CPython utilisent en fait une implémentation dict réduite (clés uniquement, pas de valeurs), donc l'attente est moyenne O(1). Comment pouvez-vous simuler des dict avec des ensembles ou quelque chose de similaire dans n'importe quelle langue? Réponse courte: avec une difficulté extrême, si vous voulez une fonctionnalité au-delà de dict.has_key(key).

3
John Machin

Mon programme semble souffrir d'un accès linéaire aux dictionnaires, son temps d'exécution croît de façon exponentielle même si l'algorithme est quadratique.

J'utilise un dictionnaire pour mémoriser des valeurs. Cela semble être un goulot d'étranglement.

C'est la preuve d'un bug dans votre méthode de mémorisation.

2
Robert Rossney

Comme d'autres l'ont souligné, l'accès aux dict en Python est rapide. Ils sont probablement la structure de données la mieux huilée du langage, étant donné leur rôle central. Le problème est ailleurs.

Combien de tuples mémorisez-vous? Avez-vous considéré l'empreinte mémoire? Vous passez peut-être tout votre temps dans l'allocateur de mémoire ou la mémoire de pagination.

1
Ned Batchelder