2010-04-25 13 views
21

Attualmente sto scrivendo un'app in python che ha bisogno di generare grandi quantità di numeri casuali, VELOCE. Attualmente ho uno schema che usa numpy per generare tutti i numeri in un batch gigante (circa ~ 500.000 alla volta). Anche se questo sembra essere più veloce dell'implementazione di Python. Ho ancora bisogno di andare più veloce. Qualche idea? Sono aperto a scriverlo in C e incorporarlo nel programma o fare w/e ci vuole.Il modo più veloce per generare oltre 1.000.000 di numeri casuali in python

vincoli sui numeri casuali:

  • Un insieme di 7 numeri che possono tutti hanno diversi limiti:
    • ad esempio: [0-X1, X2 0-, 0-X3, 0- X4, X5 0-, 0-X6, 0-X7]
    • Attualmente sto generando una lista di 7 numeri con valori casuali da [0-1) poi moltiplicando per [X1..X7]
  • Un insieme di 13 numeri che sommano tutti a 1
    • Attualmente solo la generazione di 13 numeri quindi dividendo per la loro somma

Tutte le idee? Il pre calcolo di questi numeri e la loro memorizzazione in un file lo rendono più veloce?

Grazie!

+2

È praticamente una garanzia che passare a I/O su disco non lo renderà più veloce, quindi l'approccio di archiviazione dei file non è probabilmente quello che si desidera. –

+0

qual è l'intervallo di numeri? Presumo che siano a virgola mobile? quanto più veloce devi andare? di quanta casualità hai bisogno? puoi ottenere generando N/7 numeri e ruotandoli? m – Anycorn

+0

Quanto è importante che quei numeri vengano generati casualmente quando ne hai bisogno? Sarebbe un'opzione per memorizzare forse 5 volte più numeri casuali generati in precedenza e basta selezionare un insieme casuale di quelli? – poke

risposta

11

È possibile accelerare le cose un po 'da quello che MTRW postato sopra solo facendo quello che inizialmente descritto (che genera una serie di numeri casuali e moltiplicando e dividendo di conseguenza) ...

Inoltre, probabilmente lo sai già, ma assicurati di eseguire le operazioni sul posto (* =,/=, + =, ecc.) Quando lavori con array numpy di grandi dimensioni.Fa un'enorme differenza nell'uso della memoria con array di grandi dimensioni, e darà anche un notevole aumento di velocità.

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 

Rispetto a:

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

Non è una differenza enorme, ma se siete veramente preoccupati per la velocità, si tratta di qualcosa.

solo a dimostrare che è corretto:

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]) 

Allo stesso modo, per le "righe sommare ad 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 x.T 
    ....: 

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.]) 

Onestamente, anche se si re-implementare le cose in C , Non sono sicuro che sarai in grado di batterti parecchio su questo ... Potrei sbagliarmi, però!

+0

@Joe - Ho provato il tuo metodo per i numeri limitati e ho trovato che fosse più lento sulla mia macchina. Sono curioso di poter provare il mio e confrontare? Ho anche rubato il tuo metodo per i numeri sum-to-1; era molto più veloce di quello che stavo provando prima. Grazie! – mtrw

+0

@ mtrw- Le tue funzioni aggiornate sono più veloci delle mie da un bel po ', ora. (166 ms vs 184 ms) Il tuo non richiede che l'intero blocco di memoria sia contiguo, solo la memoria per ogni colonna, che credo sia la causa principale della differenza. Lo svantaggio sta nell'accedere ai dati dopo che è stato creato. Dovrai utilizzare le list comprehensions (o simili) per il tuo, mentre il mio restituisce un singolo array di numpy 2D (indicizzazione leggermente più veloce e molto più flessibile). Non importa molto se hai solo bisogno di accedere a una riga alla volta, però. Saluti! –

+0

Grazie per il duro lavoro! Cercando di mettere insieme il codice ora ... – Sandro

6

EDIT Funzioni create che restituiscono l'intero set di numeri, non solo una riga alla volta. EDIT 2 Fai funzioni più divinatorio (e più veloce), aggiungere soluzione per la seconda domanda

Per la prima serie di numeri, si potrebbe considerare numpy.random.randint o numpy.random.uniform, che prendono low e high parametri. Generazione di una serie di 7 x 1.000.000 numeri in un intervallo specificato sembra prendere < 0,7 secondi sulla mia macchina 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) 

Ciò restituisce numeri interi in [0, Xlim-1] o galleggia in [0, Flim) . La versione intera richiedeva ~ 0,3 secondi, la doppia ~ 0,66, sulla mia macchina single-core da 2 GHz.

Per il secondo set, ho usato il suggerimento di @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) 

Questo richiede ~ 1,6 secondi.

In tutti i casi, result[k] fornisce il kth set di dati.

+0

potresti vincere pochi cicli se moltiplichi per inverso invece di dividersi in prestazioni in virgola mobile. – Anycorn

+0

Dovrò dare una scusa. Conoscete le prestazioni degli array di impilamento in orizzontale (non sapete come esprimerli) per combinare gli array? – Sandro

+0

@aaa - Grazie, ho inserito il tuo suggerimento nella risposta. @Sandro - Penso che lo stack non sia eccezionale. Potresti essere in grado di preallocare la matrice. Vedrò se posso fare quel lavoro e aggiornare la risposta. – mtrw

1

Rendere il codice in esecuzione in parallelo non potrebbe certamente far male. Prova adattandolo per SMP con Parallel Python

+2

In realtà a causa della grande memoria richiesta, copiare la memoria o inviarla su una pipe è piuttosto costosa e finora mi ha rallentato. – Sandro

3

Prova r = 1664525*r + 1013904223
da "un generatore ancora più veloce" nelle "Numerical Recipes in C" 2 ° edizione, Press et al., ISBN 0.521.431,085 mila, pag. 284.
np.random è sicuramente "più casuale"; vedi Linear congruential generator.

In Python, utilizzare np.uint32 come questo:

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 
' 
1

Come altri hanno già fatto notare, numpy è un ottimo inizio, veloce e facile da usare.

Se avete bisogno di numeri casuali su scala massiccia, considerate eas-ecb o rc4. Entrambi possono essere parallelizzati, si dovrebbe raggiungere prestazioni in diversi GB/s.

achievable numbers posted here

+0

Non penso che la tua risposta aggiunga nuove informazioni? – nemo

+0

aggiunto un collegamento ... –

-1

Solo un rapido esempio di numpy in azione:

data = numpy.random.rand(1000000) 

Non c'è bisogno di loop, è possibile passare a quanti numeri si desidera generare.

Problemi correlati