web-dev-qa-db-fra.com

Exponentielles en python x. ** y vs math.pow (x, y)

Lequel est le plus efficace en utilisant math.pow ou l'opérateur **? Quand devrais-je utiliser l'un sur l'autre?

Jusqu'à présent, je sais que x**y peut renvoyer un int ou un float si vous utilisez une décimale, la fonction pow renverra un flottant

import math

print math.pow(10, 2)

print 10. ** 2
40
user3084006

L'utilisation de l'opérateur motorisé ** Sera plus rapide car il n'aura pas la surcharge d'un appel de fonction. Vous pouvez le voir si vous démontez le code Python:

>>> dis.dis('7. ** i')
  1           0 LOAD_CONST               0 (7.0) 
              3 LOAD_NAME                0 (i) 
              6 BINARY_POWER         
              7 RETURN_VALUE         
>>> dis.dis('pow(7., i)')
  1           0 LOAD_NAME                0 (pow) 
              3 LOAD_CONST               0 (7.0) 
              6 LOAD_NAME                1 (i) 
              9 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             12 RETURN_VALUE         
>>> dis.dis('math.pow(7, i)')
  1           0 LOAD_NAME                0 (math) 
              3 LOAD_ATTR                1 (pow) 
              6 LOAD_CONST               0 (7) 
              9 LOAD_NAME                2 (i) 
             12 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             15 RETURN_VALUE         

Notez que j'utilise une variable i comme exposant ici car des expressions constantes comme 7. ** 5 Sont en fait évaluées au moment de la compilation.

Maintenant, dans la pratique, cette différence n'a pas beaucoup d'importance, comme vous pouvez le voir lors du chronométrage:

>>> from timeit import timeit
>>> timeit('7. ** i', setup='i = 5')
0.2894785532627111
>>> timeit('pow(7., i)', setup='i = 5')
0.41218495570683444
>>> timeit('math.pow(7, i)', setup='import math; i = 5')
0.5655053168791255

Ainsi, alors que pow et math.pow Sont environ deux fois plus lents, ils sont encore assez rapides pour ne pas s'en soucier. À moins que vous ne puissiez réellement identifier l'exponentiation comme un goulot d'étranglement, il n'y aura aucune raison de choisir une méthode plutôt qu'une autre si la clarté diminue. Cela s'applique particulièrement puisque pow offre une opération modulo intégrée par exemple.


Alfe a posé une bonne question dans les commentaires ci-dessus:

timeit montre que math.pow Est plus lent que ** Dans tous les cas. À quoi sert math.pow() de toute façon? Quelqu'un a-t-il une idée où cela peut être avantageux alors?

La grande différence de math.pow À la fois avec pow et l'opérateur de puissance ** Est qu'il toujours utilise la sémantique flottante. Donc, si pour une raison quelconque, vous voulez vous assurer de récupérer un flottant, alors math.pow Assurera cette propriété.

Prenons un exemple: nous avons deux nombres, i et j, et nous n'avons aucune idée s'ils sont des flottants ou des entiers. Mais nous voulons avoir un résultat flottant de i^j. Alors, quelles options avons-nous?

  • Nous pouvons convertir au moins un des arguments en un flottant puis faire i ** j.
  • Nous pouvons faire i ** j Et convertir le résultat en un flottant (l'exposant de flottement est automatiquement utilisé lorsque i ou j sont des flottants, donc le résultat est le même).
  • Nous pouvons utiliser math.pow.

Alors testons ceci:

>>> timeit('float(i) ** j', setup='i, j = 7, 5')
0.7610865891750791
>>> timeit('i ** float(j)', setup='i, j = 7, 5')
0.7930400942188385
>>> timeit('float(i ** j)', setup='i, j = 7, 5')
0.8946636625872202
>>> timeit('math.pow(i, j)', setup='import math; i, j = 7, 5')
0.5699394063529439

Comme vous pouvez le voir, math.pow Est en fait plus rapide! Et si vous y réfléchissez, la surcharge de l'appel de fonction a également disparu maintenant, car dans toutes les autres alternatives, nous devons appeler float().


De plus, il peut être utile de noter que le comportement de ** Et pow peut être remplacé en implémentant la méthode spéciale __pow__ (Et __rpow__) Pour types personnalisés. Donc, si vous ne le souhaitez pas (pour quelque raison que ce soit), utiliser math.pow Ne le fera pas.

78
poke

Juste pour le protocole: l'opérateur ** Appelle la fonction fonction pow intégrée qui accepte un troisième argument optionnel (module) si les deux premiers arguments sont des types entiers.

Donc, si vous avez l'intention de calculer les restes à partir des pouvoirs, utilisez la fonction intégrée. Le math.pow Peut vous donner de faux résultats:

import math

base = 13
exp = 100
mod = 2
print math.pow(base, exp) % mod
print pow(base, exp, mod)

Lorsque j'ai exécuté cela, j'ai obtenu 0.0 Dans le premier cas, ce qui ne peut évidemment pas être vrai, car 13 est impair (et donc tous ses pouvoirs intégraux). La version math.pow Utilise une précision limitée qui provoque une erreur.

Par souci d'équité, nous devons dire que math.pow Peut être beaucoup plus rapide:

import timeit
print timeit.timeit("math.pow(2, 100)",setup='import math')
print timeit.timeit("pow(2, 100)")

Voici ce que j'obtiens en sortie:

0.240936803195
1.4775809183

Quelques exemples en ligne

4
Wolf

Eh bien, ils sont pour différentes tâches, vraiment.

Utilisez pow (équivalent à x ** y avec deux arguments) lorsque vous voulez une arithmétique entière.

Et utilise math.pow si l'un des arguments est float et que vous voulez une sortie float.

Pour une discussion sur les différences entre pow et math.pow, voyez ceci question .

3
wim

** Est en effet plus rapide que math.pow(), mais si vous voulez une fonction quadratique simple comme dans votre exemple, il est encore plus rapide d'utiliser un produit.

10.*10.

sera plus rapide alors

10.**2

La différence n'est pas grande et n'est pas perceptible avec une seule opération (en utilisant timeit), mais avec un grand nombre d'opérations, elle peut être significative.

2
KDhoore