Quelle est la façon la plus efficace de faire pivoter une liste en python? En ce moment, j'ai quelque chose comme ça:
>>> def rotate(l, n):
... return l[n:] + l[:n]
...
>>> l = [1,2,3,4]
>>> rotate(l,1)
[2, 3, 4, 1]
>>> rotate(l,2)
[3, 4, 1, 2]
>>> rotate(l,0)
[1, 2, 3, 4]
>>> rotate(l,-1)
[4, 1, 2, 3]
Y a-t-il une meilleure façon?
rotate
c'est le bon mot, nonshift
.Réponses:
A
collections.deque
est optimisé pour tirer et pousser sur les deux extrémités. Ils ont même unerotate()
méthode dédiée .la source
collections.deque rotate()
est plus rapide que le découpage selon wiki.python.org/moin/TimeComplexitydeque.rotate
nécessite d'abord une conversion de type endeque
objet, qui est plus lente quel.append(l.pop(0))
. Donc, si vous avez un objet deque pour commencer, c'est le plus rapide. Sinon, utilisezl.append(l.pop(0))
.deque.rotate
est O (k) mais la conversion de type de liste en deque est O (n) . Donc, si vous commencez avec une liste, utiliser deque.rotate est O (n) + O (k) = O (n).l.append(l.pop(0))
d'autre part est O (1).Qu'en est-il simplement d'utiliser
pop(0)
?la source
l.append(l.pop(0)
. Si je ne me trompe pas, c'est O (1).Numpy peut le faire en utilisant la
roll
commande:la source
Cela dépend de ce que vous voulez que vous ayez à faire:
Vous voudrez peut-être changer votre:
à:
la source
La façon la plus simple à laquelle je peux penser:
la source
collections.deque
est plus rapide mais pour les cas les plus courants de longueur de liste sur une seule itération, ou tout cas d'itérations multiples,a.append(a.pop(0))
va être plus rapide que la conversion de type en dequeSi vous souhaitez simplement parcourir ces ensembles d'éléments plutôt que de construire une structure de données distincte, envisagez d'utiliser des itérateurs pour construire une expression de générateur:
la source
Cela dépend également si vous souhaitez déplacer la liste en place (en la mutant) ou si la fonction doit renvoyer une nouvelle liste. Parce que, selon mes tests, quelque chose comme ça est au moins vingt fois plus rapide que votre implémentation qui ajoute deux listes:
En fait, même ajouter un
l = l[:]
en haut de celui-ci pour fonctionner sur une copie de la liste transmise est toujours deux fois plus rapide.Diverses implémentations avec un certain timing sur http://gist.github.com/288272
la source
l[:n] = []
j'irais pourdel l[:n]
. Juste une alternative.del
est toujours une déclaration dans Py3. Cependantx.__delitem__(y) <==> del x[y]
, si vous préférez utiliser des méthodes, ill.__delitem__(slice(n))
est également équivalent et fonctionne à la fois en 2 et 3.Juste quelques notes sur le timing:
Si vous commencez avec une liste,
l.append(l.pop(0))
c'est la méthode la plus rapide que vous pouvez utiliser. Cela peut être démontré avec la seule complexité du temps:Donc, si vous commencez avec des
deque
objets, vous pouvez le fairedeque.rotate()
au prix de O (k). Mais, si le point de départ est une liste, la complexité temporelle de l'utilisationdeque.rotate()
est O (n).l.append(l.pop(0)
est plus rapide à O (1).Juste à titre d'illustration, voici quelques exemples de timings sur les itérations 1M:
Méthodes qui nécessitent une conversion de type:
deque.rotate
avec objet deque: 0,12380790710449219 secondes (le plus rapide)deque.rotate
avec conversion de type: 6,853878974914551 secondesnp.roll
avec nparray: 6.0491721630096436 secondesnp.roll
avec conversion de type: 27,558452129364014 secondesListe des méthodes mentionnées ici:
l.append(l.pop(0))
: 0,32483696937561035 secondes (le plus rapide)shiftInPlace
": 4,819645881652832 secondesLe code temporel utilisé est ci-dessous.
collections.deque
Montrer que la création de deques à partir de listes est O (n):
Si vous devez créer des objets deque:
1M itérations @ 6,853878974914551 secondes
Si vous avez déjà des objets deque:
1M itérations @ 0,12380790710449219 secondes
np.roll
Si vous devez créer des nparrays
1M itérations @ 27,558452129364014 secondes
Si vous avez déjà des nparrays:
1M itérations @ 6.0491721630096436 secondes
"Déplacer en place"
Ne nécessite aucune conversion de type
1M itérations @ 4.819645881652832 secondes
l.append (l.pop (0))
Ne nécessite aucune conversion de type
1M itérations @ 0,32483696937561035
la source
l = [random.random() for i in range(100000)]
l.append(l.pop(0))
c'est le meilleur moyen de décaler les listes courtes (environ 7 éléments) d'un?l.append(l.pop(0))
comme réponse: Cette question est fermée en double. Peut-être voterez-vous pour sa réouverture?Je me suis également intéressé à cela et j'ai comparé certaines des solutions suggérées avec perfplot (un petit projet à moi).
Il se trouve que
est de loin la méthode la plus rapide pour les petites équipes
n
.Pour les plus grands
n
,n'est pas mauvais.
Essentiellement, perfplot effectue le changement pour augmenter les grands tableaux et mesure le temps. Voici les résultats:
shift = 1
:shift = 100
:Code pour reproduire l'intrigue:
la source
l.append(l.pop(0))
comme réponse: Cette question est fermée en double. Peut-être voterez-vous pour sa réouverture?Peut-être qu'un ringbuffer est plus approprié. Ce n'est pas une liste, bien qu'il soit probable qu'elle puisse se comporter assez comme une liste pour vos besoins.
Le problème est que l'efficacité d'un décalage sur une liste est O (n), ce qui devient significatif pour des listes suffisamment grandes.
Le passage dans un ringbuffer met simplement à jour l'emplacement de la tête qui est O (1)
la source
Pour une implémentation immuable, vous pouvez utiliser quelque chose comme ceci:
la source
Si l'efficacité est votre objectif, (cycles? Mémoire?), Vous feriez peut-être mieux de regarder le module de tableau: http://docs.python.org/library/array.html
Les tableaux n'ont pas la surcharge des listes.
En ce qui concerne les listes pures, ce que vous avez est aussi bon que vous pouvez l'espérer.
la source
Je pense que vous cherchez ceci:
la source
Une autre alternative:
la source
Je prends ce modèle de coût comme référence:
http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model
Votre méthode de découpage de la liste et de concaténation de deux sous-listes est une opération à temps linéaire. Je suggère d'utiliser pop, qui est une opération à temps constant, par exemple:
la source
collections.dequeue
pop et appendleft, qui sont tous les deux O (1) ops. Dans ma première réponse ci-dessus, insérer est O (n).collections.deque
Je ne sais pas si c'est «efficace», mais ça marche aussi:
EDIT: Bonjour encore une fois, je viens de trouver un gros problème avec cette solution! Considérez le code suivant:
La méthode shift_classlist () exécute le même code que ma solution x.insert (0, x.pop ()) - otherlist est une liste indépendante de la classe. Après avoir transmis le contenu de l'autre liste à la liste MyClass.classlist, l'appel de shift_classlist () modifie également l'autre liste:
SORTIE CONSOLE:
J'utilise Python 2.7. Je ne sais pas si c'est un bug, mais je pense qu'il est plus probable que j'ai mal compris quelque chose ici.
Est-ce que quelqu'un d'entre vous sait pourquoi cela se produit?
la source
x.classlist = otherlist
faitx.classlist
référence à la même listeotherlist
, puis lorsque vous l'appelez,x.shift_classlist()
il mute la liste et parce que les deux noms font référence au même objet de liste. Les deux noms semblent changer car ce ne sont que des alias pour le même objet. Utilisezx.classlist = otherlist[:]
plutôt pour attribuer une copie de la liste.La méthode suivante est O (n) en place avec une mémoire auxiliaire constante:
Notez qu'en python, cette approche est horriblement inefficace par rapport aux autres car elle ne peut tirer parti des implémentations natives d'aucune des pièces.
la source
J'ai une chose similaire. Par exemple, pour décaler de deux ...
la source
Je pense que vous avez le moyen le plus efficace
la source
Quel est le cas d'utilisation? Souvent, nous n'avons pas réellement besoin d'un tableau entièrement décalé - nous avons juste besoin d'accéder à une poignée d'éléments dans le tableau décalé.
L'obtention de tranches Python est exécutée O (k) où k est la tranche, donc une rotation en tranches est exécutée N. La commande deque rotation est également O (k). Pouvons-nous faire mieux?
Considérons un tableau qui est extrêmement grand (disons, si grand qu'il serait lent à calculer pour le découper). Une solution alternative serait de laisser le tableau d'origine seul et de simplement calculer l'indice de l'élément qui aurait existé dans notre indice souhaité après un changement quelconque.
Accéder à un élément décalé devient ainsi O (1).
la source
La fonction suivante copie la liste envoyée à un templiste, afin que la fonction pop n'affecte pas la liste d'origine:
Essai:
Production:
la source
Jon Bentley dans Programming Pearls (Colonne 2) décrit un algorithme élégant et efficace pour faire tourner un
n
vecteur -élémentx
laissé par desi
positions:Cela peut être traduit en Python comme suit:
Démo:
la source
Pour une liste
X = ['a', 'b', 'c', 'd', 'e', 'f']
et une valeur de décalage souhaitéeshift
inférieure à la longueur de la liste , nous pouvons définir la fonctionlist_shift()
comme ci-dessousExemples,
list_shift(X,1)
retourne['b', 'c', 'd', 'e', 'f', 'a']
list_shift(X,3)
retourne['d', 'e', 'f', 'a', 'b', 'c']
la source
list_shift
dans votre réponse est identique à la fonctionshift
dans la question d'origine, donc ce n'est pas une réponse à la question réelle: "Y a-t-il une meilleure façon?"Par exemple, étant donné
la fonction devrait revenir
[9, 7, 6, 3, 8]
. Trois rotations ont été effectuées:Pour un autre exemple, étant donné
la fonction devrait retourner
[0, 0, 0]
Donné
la fonction devrait retourner
[1, 2, 3, 4]
la source
Je cherchais une solution en place à ce problème. Cela résout le but de O (k).
la source
pour des fonctionnalités similaires à celles des changements dans d'autres langues:
la source
L.pop(0)