2013-07-19 8 views
4

Yat-il un moyen d'accélérer une double boucle qui met à jour ses valeurs de l'itération précédente?Accélérez double boucle en Python

Dans le code:

def calc(N, m): 
    x = 1.0 
    y = 2.0 
    container = np.zeros((N, 2)) 
    for i in range(N): 
     for j in range(m): 
     x=np.random.gamma(3,1.0/(y*y+4)) 
     y=np.random.normal(1.0/(x+1),1.0/sqrt(x+1)) 
     container[i, 0] = x 
     container[i, 1] = y 
    return container 

calc(10, 5) 

Comme vous pouvez le voir, la boucle intérieure est mise à jour des variables x et y tandis que la boucle externe commence par une valeur différente de x à chaque fois. Je ne pense pas que cela soit vectorisable mais peut-être y a-t-il d'autres améliorations possibles.

Merci!

+0

« la seconde boucle est mise à jour des variables x et y tandis que la première boucle commence par une valeur différente de x à chaque fois » - Je ne suis pas sûr de ce que vous signifie par là. En particulier, je ne sais pas ce que vous entendez par "commence avec une valeur différente de x à chaque fois", et "première" ou "deuxième" boucle est plutôt floue par rapport à "interne" ou "externe". Je ne suis pas tout à fait sûr de savoir lequel est quoi. – user2357112

+0

Désolé, je ne peux pas penser à beaucoup plus que de remplacer 'range' par' xrange'. Peut-être que vous devriez mettre une prime sur la question pour attirer un expert «numpy» – inspectorG4dget

+0

Désolé. Tu as raison. Je vais changer la première ou la deuxième boucle. Par _start avec une valeur différente de x à chaque fois_, je veux dire que la première itération de la boucle externe commence par x = 1.0. Cependant, x est modifié dans la boucle interne. Par conséquent, dans la deuxième itération de la boucle externe, x est une autre valeur. –

Répondre

1

Je ne pense pas que ça va ajouter à une vitesse importante, mais vous pouvez économiser des appels de fonction si vous générez tous vos gamma et des valeurs aléatoires normalement distribué à la fois.

fonctions gamma ont une scaling property, de sorte que si on trace une valeur x d'une distribution gamma (k, 1), puis c*x aura une valeur tirée d'une distribution gamma (k, c). De même, la distribution normale, vous pouvez prendre une valeur y tirée d'une normale (0, 1) la distribution et le convertir en une valeur tirée d'une distribution normale (m, s) faisant x*s + m. Ainsi, vous pouvez réécrire votre fonction comme suit:

def calc(N, m): 
    x = 1.0 
    y = 2.0 
    container = np.zeros((N, 2)) 
    nm = N*m 
    gamma_vals = np.random.gamma(3, 1, size=(nm,)) 
    norm_vals = np.random.normal(0, 1, size=(nm,)) 
    for i in xrange(N): 
     for j in xrange(m): 
      ij = i*j 
      x = gamma_vals[ij]/(y*y+4) 
      y = norm_vals[ij]/np.sqrt(x+1) + 1/(x+1) 
     container[i, 0] = x 
     container[i, 1] = y 
    return container 

Si les paramètres réels de vos distributions avaient une expression plus simple, vous pouvez effectivement être en mesure d'utiliser une forme élaborée de np.cumprod ou similaires, et vous épargner les boucles. Je ne suis pas en mesure de trouver un moyen de le faire ...

+0

Malheureusement, c'est 2x plus lent que l'approche par np.random.gamma et np.random.normal dans la boucle intérieure. Peut-être qu'il y a une erreur parce que ça ne donne pas le même résultat. –

+0

Vous dessinez des nombres aléatoires, ce n'est donc pas une grande surprise que les résultats soient différents. Même si vous avez ensemencé le générateur de nombres aléatoires, vous les utilisez dans un ordre différent, donc encore une fois, très normal. En ce qui concerne la performance, ce serait une astuce à utiliser si elle permettait de supprimer les boucles, ce qui ne semble pas possible avec votre problème. – Jaime

+0

1+. Tu as raison. Merci. Pensez-vous qu'il n'y a aucun moyen d'optimiser cela davantage? –

0

ce travail?

for i in xrange(N): 
    # xrange is an iterator, range makes a new list. 
    # You save linear space and `malloc`ing time by doing this 

    x += m*y # a simple algebra hack. Compute this line of the loop just once instead of `m` times 
    y -= m*x 
    y *= -1 # another simple algebra hack. Compute this line of the loop just once instead of `m` times 
    container[i,0] = x 
    container[i,1] = y 
return container 
+0

Merci pour votre réponse. xrange est une bonne idée. Cependant, la partie de l'algèbre ne s'applique pas à la boucle réelle que je suis en train d'optimiser (il implique des fonctions gamma) –

+0

@RobertSmith: désolé, je ne peux pas penser à bien d'autre en ce moment – inspectorG4dget

+0

@RobertSmith: Pouvez-vous poster la vrai code? – user2357112