Disons que j'ai une liste x
avec une longueur inconnue à partir de laquelle je veux extraire au hasard un élément afin que la liste ne contienne pas l'élément par la suite. Quelle est la façon la plus Pythonique de procéder?
Je peux le faire en utilisant une combinaison plutôt peu pratique de pop
, random.randint
et len
, et souhaiteraient des solutions plus courtes ou plus agréables:
import random
x = [1,2,3,4,5,6]
x.pop(random.randint(0,len(x)-1))
Ce que j'essaie de réaliser, c'est de faire apparaître successivement des éléments aléatoires dans une liste. (c'est-à-dire, pop aléatoirement un élément et le déplacer vers un dictionnaire, pop aléatoirement un autre élément et le déplacer vers un autre dictionnaire, ...)
Notez que j'utilise Python 2.6 et que je n'ai trouvé aucune solution via la fonction de recherche.
Ce que vous semblez faire ne semble pas très pythonique en premier lieu. Vous ne devez pas supprimer des éléments du milieu d'une liste, car les listes sont implémentées sous forme de tableaux dans toutes les implémentations Python que je connais, il s'agit donc d'une opération O(n)
.
Si vous avez vraiment besoin de cette fonctionnalité dans le cadre d'un algorithme, vous devriez vérifier une structure de données comme blist
qui prend en charge la suppression efficace du milieu.
En Python pur, ce que vous pouvez faire si vous n'avez pas besoin d'accéder aux éléments restants est simplement de mélanger d'abord la liste, puis d'itérer dessus:
lst = [1,2,3]
random.shuffle(lst)
for x in lst:
# ...
Si vous avez vraiment besoin du reste (qui est un peu une odeur de code, à mon humble avis), au moins vous pouvez pop()
à partir du fin de la liste maintenant (ce qui est rapide!):
while lst:
x = lst.pop()
# do something with the element
En général, vous pouvez souvent exprimer vos programmes plus élégamment si vous utilisez un style plus fonctionnel, au lieu de l'état de mutation (comme vous le faites avec la liste).
Vous n'obtiendrez pas beaucoup mieux que cela, mais voici une légère amélioration:
x.pop(random.randrange(len(x)))
Documentation sur random.randrange()
:
random.randrange ([start], stop [ step])
Renvoie un élément sélectionné au hasard à partir derange(start, stop, step)
. Cela équivaut àchoice(range(start, stop, step))
, mais ne construit pas réellement un objet range.
Voici une autre alternative: pourquoi ne pas mélanger la liste premier, puis commencer à en faire apparaître des éléments jusqu'à ce qu'il ne reste plus d'éléments? comme ça:
import random
x = [1,2,3,4,5,6]
random.shuffle(x)
while x:
p = x.pop()
# do your stuff with p
Pour supprimer un élément nique à un index aléatoire d'une liste si l'ordre des autres éléments de la liste n'a pas d'importance:
import random
L = [1,2,3,4,5,6]
i = random.randrange(len(L)) # get random index
L[i], L[-1] = L[-1], L[i] # swap with the last element
x = L.pop() # pop last element O(1)
Le swap est utilisé pour éviter le comportement O(n) lors de la suppression d'un milieu de liste).
Sans sortir de la liste, j'ai rencontré cette question sur Google en essayant d'obtenir X éléments aléatoires d'une liste sans doublons. Voici ce que j'ai finalement utilisé:
items = [1, 2, 3, 4, 5]
items_needed = 2
from random import shuffle
shuffle(items)
for item in items[:items_needed]:
print(item)
Cela peut être légèrement inefficace car vous mélangez une liste entière mais n'en utilisez qu'une petite partie, mais je ne suis pas un expert en optimisation, donc je peux me tromper.
Une façon de le faire est:
x.remove(random.choice(x))
Cette réponse est une gracieuseté de @ niklas-b :
" Vous voulez probablement utiliser quelque chose comme pypi.python.org/pypi/blist"
Pour citer la page PYPI :
... un type de liste avec de meilleures performances asymptotiques et des performances similaires sur de petites listes
Le blist remplace les listes Python qui offrent de meilleures performances lors de la modification de grandes listes. Le package blist fournit également sortedlist, sortedset, faiblessesortedlist, faiblesortedset, sorteddict et btuple.
On supposerait une baisse des performances sur l'accès aléatoire/fin d'exécution aléatoire , car il s'agit d'une "copie sur l'écriture "structure de données. Cela viole de nombreuses hypothèses de cas d'utilisation sur les listes Python, donc utilisez-les avec précaution .
CEPENDANT, si votre cas d'utilisation principal est de faire quelque chose de bizarre et non naturel avec une liste (comme dans l'exemple forcé donné par @OP, ou mon Python 2.6 FIFO = problème de file d'attente avec transfert), cela conviendra parfaitement.
Je sais que c'est une vieille question, mais juste pour la documentation:
Si vous (la personne qui google la même question) faites ce que je pense que vous faites, c'est-à-dire sélectionner k nombre d'éléments au hasard dans une liste (où k <= len (votre liste)), mais en vous assurant que chaque élément n'est jamais sélectionné plus plusieurs fois (= échantillonnage sans remplacement), vous pouvez utiliser random.sample comme le suggère @ jf-sebastian. Mais sans en savoir plus sur le cas d'utilisation, je ne sais pas si c'est ce dont vous avez besoin.