71 votes

Créer une liste aléatoire d'entiers en Python

J'aimerais créer une liste aléatoire d'entiers à des fins de test. La distribution des nombres n'est pas importante. La seule chose qui compte est temps . Je sais que la génération de nombres aléatoires est une tâche qui prend du temps, mais il doit y avoir un meilleur moyen.

Voici ma solution actuelle :

import random
import timeit

# Random lists from [0-999] interval
print [random.randint(0, 1000) for r in xrange(10)] # v1
print [random.choice([i for i in xrange(1000)]) for r in xrange(10)] # v2

# Measurement:
t1 = timeit.Timer('[random.randint(0, 1000) for r in xrange(10000)]', 'import random') # v1
t2 = timeit.Timer('random.sample(range(1000), 10000)', 'import random') # v2

print t1.timeit(1000)/1000
print t2.timeit(1000)/1000

La v2 est plus rapide que la v1, mais elle ne fonctionne pas à une si grande échelle. Elle donne l'erreur suivante :

ValueError : échantillon plus grand que la population

Existe-t-il une solution rapide et efficace qui fonctionne à cette échelle ?

Quelques résultats de la réponse

Celle d'Andrew : 0.000290962934494

de gnibbler : 0.0058455221653

KennyTM's : 0.00219276118279

NumPy est venu, a vu, et a conquis.

4 votes

Bien sûr que ça ne marche pas. random.sample() appauvrit la population, rendant les chiffres de moins en moins aléatoires. Une fois que la population entière est épuisée, il est impossible d'échantillonner davantage.

0 votes

Quand vous dites que c'est à des fins de test, combien de temps dureront les tests ?

0 votes

Pour les simulations, où le temps est un impératif (mais pas la cryptographie ni la sécurité), une Générateur contructif linéaire (LCG) est souvent utilisé. Je crois qu'un Twister de Mersenne est rapide (mais plus lent que LCG), et il fournit une distribution uniforme, si je me souviens bien.

61voto

Andrew Jaffe Points 9205

Ce n'est pas tout à fait clair ce que vous voulez, mais j'utiliserais numpy.random.randint :

import numpy.random as nprnd
import timeit

t1 = timeit.Timer('[random.randint(0, 1000) for r in xrange(10000)]', 'import random') # v1

### Change v2 so that it picks numbers in (0, 10000) and thus runs...
t2 = timeit.Timer('random.sample(range(10000), 10000)', 'import random') # v2
t3 = timeit.Timer('nprnd.randint(1000, size=10000)', 'import numpy.random as nprnd') # v3

print t1.timeit(1000)/1000
print t2.timeit(1000)/1000
print t3.timeit(1000)/1000

qui donne sur ma machine :

0.0233682730198
0.00781716918945
0.000147947072983

Notez que randint est très différent de random.sample (pour que cela fonctionne dans votre cas, j'ai dû changer les 1 000 en 10 000, comme l'a fait remarquer l'un des commentateurs - si vous voulez vraiment qu'ils aillent de 0 à 1 000, vous pouvez diviser par 10).

Et si vous ne vous souciez vraiment pas de la distribution que vous obtenez, alors il est possible que vous ne compreniez pas très bien votre problème, ou que vous utilisiez des nombres aléatoires - je m'excuse si cela semble grossier...

3 votes

+1 pour numpy, si Stiggo a besoin de tant de nombres aléatoires, cela vaut probablement la peine d'installer numpy juste pour cela.

0 votes

Andrew, vous avez tout à fait raison en ce qui concerne la distribution. Mais ce n'est pas une chose réelle. Juste un défi entre amis :D A la vôtre !

33voto

gnibbler Points 103484

Toutes les méthodes aléatoires finissent par appeler random.random() donc le meilleur moyen est de l'appeler directement :

[int(1000*random.random()) for i in xrange(10000)]

Par exemple,

  • random.randint appelle random.randrange .
  • random.randrange a un tas de surcharge pour vérifier la gamme avant de retourner istart + istep*int(self.random() * n) .

NumPy est encore beaucoup plus rapide, bien sûr.

0 votes

+1 J'étais en train de fouiller dans tout ça plus tôt et j'ai fini par penser que randrange a finalement conduit à un appel à getrandbits . Je n'ai pas vu que vous deviez instancier SystemRandom pour que ce soit le comportement. Merci de m'avoir fait regarder de plus près.

0 votes

La vôtre bat ma version, mais la solution d'Andrew est clairement la gagnante.

1 votes

@Stiggo, c'est sûr, la seule raison à laquelle je peux penser pour ne pas utiliser numpy serait si numpy n'est pas supporté par votre plateforme, par exemple google app engine.

6voto

Colonel Panic Points 18390

Votre question sur les performances est sans objet : les deux fonctions sont très rapides. La vitesse de votre code sera déterminée par les éléments suivants faire avec les numéros aléatoires.

Cependant, il est important que vous compreniez la différence entre comportement de ces deux fonctions. L'une effectue un échantillonnage aléatoire avec remplacement, l'autre un échantillonnage aléatoire sans remplacement.

3voto

KennyTM Points 232647

Tout d'abord, vous devez utiliser randrange(0,1000) ou randint(0,999) pas randint(0,1000) . La limite supérieure de randint est inclusif.

Pour être efficace, randint est simplement une enveloppe de randrange qui appelle random vous devez donc utiliser random . Utilisez également xrange comme argument à sample pas range .

Vous pourriez utiliser

[a for a in sample(xrange(1000),1000) for _ in range(10000/1000)]

pour générer 10 000 nombres dans la plage en utilisant sample 10 fois.

(Bien sûr, cela ne battra pas NumPy).

$ python2.7 -m timeit -s 'from random import randrange' '[randrange(1000) for _ in xrange(10000)]'
10 loops, best of 3: 26.1 msec per loop

$ python2.7 -m timeit -s 'from random import sample' '[a%1000 for a in sample(xrange(10000),10000)]'
100 loops, best of 3: 18.4 msec per loop

$ python2.7 -m timeit -s 'from random import random' '[int(1000*random()) for _ in xrange(10000)]' 
100 loops, best of 3: 9.24 msec per loop

$ python2.7 -m timeit -s 'from random import sample' '[a for a in sample(xrange(1000),1000) for _ in range(10000/1000)]'
100 loops, best of 3: 3.79 msec per loop

$ python2.7 -m timeit -s 'from random import shuffle
> def samplefull(x):
>   a = range(x)
>   shuffle(a)
>   return a' '[a for a in samplefull(1000) for _ in xrange(10000/1000)]'
100 loops, best of 3: 3.16 msec per loop

$ python2.7 -m timeit -s 'from numpy.random import randint' 'randint(1000, size=10000)'
1000 loops, best of 3: 363 usec per loop

Mais puisque vous ne vous souciez pas de la distribution des nombres, pourquoi ne pas simplement utiliser :

range(1000)*(10000/1000)

?

0 votes

randrange(1000) prend plus de deux fois plus de temps que 1000*int(random()) sur mon ordinateur

0 votes

Quel est le but de 10000/1000 ?

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