I'm curious as to why it's so much faster to multiply than to take powers in python (though from what I've read this may well be true in many other languages too). For example it's much faster to do
I suppose the ** operator is more general and can also deal with fractional powers. But if that's why it's so much slower, why doesn't it perform a check for an int exponent and then just do the multiplication?
Edit: Here's some example code I tried...
def pow1(r, n): for i in range(r): p = i**n def pow2(r, n): for i in range(r): p = 1 for j in range(n): p *= i
Now, pow2 is just a quick example and is clearly not optimised!
But even so I find that using n = 2 and r = 1,000,000, then pow1 takes ~ 2500ms and pow2 takes ~ 1700ms.
I admit that for large values of n, then pow1 does get much quicker than pow2. But that's not too surprising.
How to solve the “Mastermind” guessing game?
Whats the algorithm to calculate aspect ratio? I need an output like: 4:3, 16:9
Sliding window algorithm in C#
Taking the power is O(log n) with a higher constant factor (There are special cases that need to be tested...
Algorithm for a graph problem
fractional exponents, negative exponents, etc) .
Need for predictable random generator
Edit: just to be clear, that's O(n) where n is the exponent..
Metric to Compare how Similar Two 2D Linear Lines Are
Of course the naive approach will be faster for small n, you're only really implementing a small subset of exponential math so your constant factor is negligible.
Find a number where it appears exactly N/2 times
How to find overlapped items?
Do you always want that check there? A compiled language could make the check for a constant exponent to see if it's a relatively small integer because there's no run-time cost, just a compile-time cost.
An interpreted language might not make that check.. It's up to the particular implementation unless that kind of detail is specified by the language.. Python doesn't know what distribution of exponents you're going to feed it.
If it's going to be 99% non-integer values, do you want the code to check for an integer every time, making runtime even slower?.
However, in cases where the exponent is known in advance( eg.
literal 2 is used), the bytecode generated could be optimised with a simple peephole optimisation.
Presumably this simply hasn't been considered worth doing (it's a fairly specific case).
. Here's a quick proof of concept that does such an optimisation (usable as a decorator).
Note: you'll need the byteplay module to run it..
Which gives the timings:.
import byteplay, timeit def optimise(func): c = byteplay.Code.from_code(func.func_code) prev=None for i, (op, arg) in enumerate(c.code): if op == byteplay.BINARY_POWER: if c.code[i-1] == (byteplay.LOAD_CONST, 2): c.code[i-1] = (byteplay.DUP_TOP, None) c.code[i] = (byteplay.BINARY_MULTIPLY, None) func.func_code = c.to_code() return func def square(x): return x**2 print "Unoptimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000) square = optimise(square) print "Optimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000)
[Edit] Actually, thinking about it a bit more, there's a very good reason why this optimisaton isn't done.
Unoptimised : 6.42024898529 Optimised : 4.52667593956
There's no guarantee that someone won't create a user defined class that overrides the
__pow__methods and do something different for each.
The only way to do it safely is if you can guarantee that the object on the top of the stack is one that has the same result "x
**2" and "
x*x", but working that out is much harder.
in my example it's impossible, as any object could be passed to the square function..
Typically, if you want to do serious calculations, you do them in Fortran or C or C++ or something like that (and perhaps call them from Python).. Treating everything as exp(n * log(x)) works well in cases where n isn't integral or is pretty large, but is relatively inefficient for small integers.
Checking to see if n is a small enough integer does take time, and adds complication.. Whether the check is worth it depends on the expected exponents, how important it is to get best performance here, and the cost of the extra complexity.
Apparently, Guido and the rest of the Python gang decided the check wasn't worth doing.. If you like, you could write your own repeated-multiplication function..
def power(b, p): """ Calculates b^p Complexity O(log p) b -> double p -> integer res -> double """ res = 1 while p: if p & 0x1: res *= b b *= b p >>= 1 return res
but the number where actual crossover occurs depends on various conditions, so in my opinion, that's why the optimization was not done(or couldn't be done) in language/library level.
But users can still optimize for some special cases :).