La manera más rápida de generar más de 1,000,000 de números aleatorios en python

Actualmente estoy escribiendo una aplicación en python que necesita generar una gran cantidad de números aleatorios, FAST. Actualmente tengo un esquema que utiliza numpy para generar todos los números en un lote gigante (alrededor de ~ 500,000 a la vez). Si bien esto parece ser más rápido que la implementación de python. Todavía lo necesito para ir más rápido. ¿Algunas ideas? Estoy abierto a escribirlo en C e integrarlo en el progtwig o hacer lo que sea necesario.

Restricciones en los números aleatorios:

  • Un conjunto de 7 números que pueden tener diferentes límites:
    • por ejemplo: [0-X1, 0-X2, 0-X3, 0-X4, 0-X5, 0-X6, 0-X7]
    • Actualmente estoy generando una lista de 7 números con valores aleatorios de [0-1) y luego multiplicándolos por [X1..X7]
  • Un conjunto de 13 números que sumn 1.
    • Actualmente solo generando 13 números y luego dividiendo por su sum.

¿Algunas ideas? ¿El cálculo previo de estos números y su almacenamiento en un archivo lo haría más rápido?

¡Gracias!

Puedes acelerar un poco las cosas de lo que mtrw publicó anteriormente solo haciendo lo que describiste inicialmente (generando un grupo de números aleatorios y multiplicando y dividiendo en consecuencia) …

Además, probablemente ya lo sepa, pero asegúrese de realizar las operaciones en el lugar (* =, / =, + =, etc.) cuando trabaje con matrices de gran tamaño. Hace una gran diferencia en el uso de la memoria con arreglos grandes, y también dará un aumento de velocidad considerable.

 In [53]: def rand_row_doubles(row_limits, num): ....: ncols = len(row_limits) ....: x = np.random.random((num, ncols)) ....: x *= row_limits ....: return x ....: In [59]: %timeit rand_row_doubles(np.arange(7) + 1, 1000000) 10 loops, best of 3: 187 ms per loop 

En comparación con:

 In [66]: %timeit ManyRandDoubles(np.arange(7) + 1, 1000000) 1 loops, best of 3: 222 ms per loop 

No es una gran diferencia, pero si estás realmente preocupado por la velocidad, es algo.

Solo para demostrar que es correcto:

 In [68]: x.max(0) Out[68]: array([ 0.99999991, 1.99999971, 2.99999737, 3.99999569, 4.99999836, 5.99999114, 6.99999738]) In [69]: x.min(0) Out[69]: array([ 4.02099599e-07, 4.41729377e-07, 4.33480302e-08, 7.43497138e-06, 1.28446819e-05, 4.27614385e-07, 1.34106753e-05]) 

Del mismo modo, para su “filas sum a una” parte …

 In [70]: def rand_rows_sum_to_one(nrows, ncols): ....: x = np.random.random((ncols, nrows)) ....: y = x.sum(axis=0) ....: x /= y ....: return xT ....: In [71]: %timeit rand_rows_sum_to_one(1000000, 13) 1 loops, best of 3: 455 ms per loop In [72]: x = rand_rows_sum_to_one(1000000, 13) In [73]: x.sum(axis=1) Out[73]: array([ 1., 1., 1., ..., 1., 1., 1.]) 

Honestamente, incluso si vuelves a implementar las cosas en C, no estoy seguro de que puedas superar mucho en este caso … ¡aunque podría estar muy equivocado!

EDITAR Funciones creadas que devuelven el conjunto completo de números, no solo una fila a la vez. EDIT 2 Haga que las funciones sean más pythonicas (y más rápidas), agregue solución para la segunda pregunta

Para el primer conjunto de números, podría considerar numpy.random.randint o numpy.random.uniform , que toman parámetros high y low . La generación de una matriz de 7 x 1,000,000 de números en un rango específico parece tomar <0.7 segundos en mi máquina de 2 GHz:

 def LimitedRandInts(XLim, N): rowlen = (1,N) return [np.random.randint(low=0,high=lim,size=rowlen) for lim in XLim] def LimitedRandDoubles(XLim, N): rowlen = (1,N) return [np.random.uniform(low=0,high=lim,size=rowlen) for lim in XLim] >>> import numpy as np >>> N = 1000000 #number of randoms in each range >>> xLim = [x*500 for x in range(1,8)] #convenient limit generation >>> fLim = [x/7.0 for x in range(1,8)] >>> aa = LimitedRandInts(xLim, N) >>> ff = LimitedRandDoubles(fLim, N) 

Esto devuelve números enteros en [0, xLim-1] o flota en [0, fLim). La versión entera tomó ~ 0.3 segundos, el doble ~ 0.66, en mi máquina de un solo núcleo de 2 GHz.

Para el segundo set, utilicé la sugerencia de @Joe Kingston.

 def SumToOneRands(NumToSum, N): aa = np.random.uniform(low=0,high=1.0,size=(NumToSum,N)) #13 rows by 1000000 columns, for instance s = np.reciprocal(aa.sum(0)) aa *= s return aa.T #get back to column major order, so aa[k] is the kth set of 13 numbers >>> ll = SumToOneRands(13, N) 

Esto lleva ~ 1,6 segundos.

En todos los casos, el result[k] le da el k conjunto de datos.

Prueba r = 1664525*r + 1013904223
de “un generador aún más rápido” en “Recetas numéricas en C” 2da edición, Press et al., isbn 0521431085, p. 284.
np.random es ciertamente “más aleatorio”; Ver generador congruente lineal .

En Python, use np.uint32 como este:

 python -mtimeit -s ' import numpy as np r = 1 r = np.array([r], np.uint32)[0] # 316 py -> 16 us np # python longs can be arbitrarily long, so slow ' ' r = r*1664525 + 1013904223 # NR2 p. 284 ' 

Para generar grandes bloques a la vez:

 # initialize -- np.random.seed( ... ) R = np.random.randint( 0, np.iinfo( np.uint32 ).max, size, dtype=np.uint32 ) ... R *= 1664525 R += 1013904223 

Hacer que su código se ejecute en paralelo ciertamente no podría hacer daño. Intenta adaptarlo para SMP con Parallel Python

Como ya han numpy , numpy es un muy buen comienzo, rápido y fácil de usar.

Si necesita números aleatorios en una escala masiva, considere eas-ecb o rc4. Ambos pueden ser paralelizados, debe alcanzar el rendimiento en varios GB / s.

Números alcanzables publicados aquí

Solo un rápido ejemplo de numpy en acción:

 data = numpy.random.rand(1000000) 

Sin necesidad de bucle, puede pasar la cantidad de números que desea generar.