Les exponentielles en python x.**y vs mathématiques.pow(x, y)
qui est plus efficace en mathématiques.pow Ou l'opérateur**? Quand dois-je utiliser l'un plutôt que l'autre?
Jusqu'à présent je sais que x**y
peut retourner un int
ou un float
si vous utilisez une décimale
la fonction pow
retournera un flotteur
import math
print math.pow(10, 2)
print 10. ** 2
4 réponses
utilisant l'opérateur de puissance **
sera plus rapide car il n'aura pas le plafond d'un appel de fonction. Vous pouvez le voir si vous désassemblez 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 parce que 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 en chronométrant:
>>> 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
donc, alors que pow
et math.pow
sont environ deux fois plus lents, ils sont encore assez rapide pour ne pas s'en soucier beaucoup. À moins que vous ne puissiez réellement identifier l'exponentiation comme un goulot d'étranglement, il n'y aura pas de raison de choisir une méthode plutôt que l'autre si la clarté diminue. Cela s'applique particulièrement puisque pow
offre une opération de modulo intégrée par exemple.
Alfe a demandé une bonne question dans les commentaires ci-dessus:
timeit
montre quemath.pow
est plus lent que**
dans tous les cas. À quoi sertmath.pow()
de toute façon? Personne n'a une idée d'où il peut être de n'importe quel avantage?
la grande différence de math.pow
à la fois le bâti pow
et l'opérateur de puissance **
est qu'il toujours utilise la sémantique de flotteur. Donc, si vous, pour une raison quelconque, voulez vous assurer que vous obtenez un flottant comme un résultat de retour, puis math.pow
assurera cette propriété.
pensons à un exemple: nous avons deux nombres, i
et j
, et n'avons aucune idée si ce sont des flotteurs ou des entiers. Mais nous voulons un float résultat de i^j
. Alors, quelles options avons-nous?
- nous pouvons convertir au moins un des arguments en un float et ensuite faire
i ** j
. - nous pouvons faire
i ** j
et convertir le résultat en un flotteur (float exponentation est automatiquement utilisé lorsquei
ouj
sont des flotteurs, de sorte que le résultat est le même). - on peut utiliser
math.pow
.
donc, 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 pensez, le overhead de l'appel de fonction est aussi parti maintenant, parce que dans toutes les autres alternatives, nous devons appeler float()
.
en outre, il pourrait être intéressant de noter que le comportement de **
et pow
peut être dépassé en mettant en œuvre la méthode spéciale __pow__
(et __rpow__
) pour les types personnalisés. Donc, si vous ne le voulez pas (pour une raison quelconque), utiliser math.pow
ne le fera pas.
juste pour le protocole: l'opérateur **
appelle la 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 des puissances, 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)
quand j'ai couru ceci, j'ai eu 0.0
dans le premier cas qui évidemment ne peut pas être vrai, parce que 13 est étrange (et donc tous ses pouvoirs intégraux). La version math.pow
utilise une précision limitée qui provoque une erreur.
pour des raisons d'équité, nous devons dire, 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 je reçois en sortie:
0.240936803195
1.4775809183
quelques exemples en ligne
- http://ideone.com/qaDWRd (reste erroné avec
math.pow
) - http://ideone.com/g7J9Un (performances inférieures avec
pow
sur les valeurs int) - http://ideone.com/KnEtXj (performances légèrement inférieures avec
pow
sur les valeurs des flotteurs)
Eh bien, ils sont pour différentes tâches, vraiment.
utiliser pow
(équivalent à x ** y
avec deux arguments) lorsque vous voulez arithmétique entière.
et utilisez math.pow
si l'un ou l'autre des arguments est float, et vous voulez la sortie float.
pour une discussion sur les différences entre pow
et math.pow
, voir cette question .
**
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 que
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.