аватар question@mail.ru · 01.01.1970 03:00

What is the difference between different ways to extract the root in Python?

Root in python can be extracted in three ways x ** 0.5 , pow (x, 0.5) , math.SQRT (x) . And for the whole with the result, also math.isqrt (x) . What is the difference between them?

p.s. For example, when checking simplicity n you need to make a cycle to a square root of n . So right?

   for  i  in   range  ( 2 ,  int  (n **  0.5 ) +  1 ): ) ...     

We are primarily about the correctness (accuracy of calculations). Performance in second place.

аватар answer@mail.ru · 01.01.1970 03:00

Unfortunately, Google Colab does not have the math.isqrt function, because Python 3.7.11 is there. As for the rest of the functions, you can check the speed like this (a small loop is added so that there is no caching, otherwise without a cycle %timeit swears that the results may have been cached, although they are about the same, I checked, but it's better to take precautions):

import mathn =hn = 100%timeit [x ** 0.5 class="">0.5  for x in range(n)]%timeit [pow(x, 0.5) for"">for x in range(n)]%timeit [math.sqrt(x) fo="">for x in range(n)]

Output:

100,000 loops, best of 5: 17.7 µs per loop10000 loops, best of 5: 23.9 µs per loop100,000 loops, best of 5: 13.2 µs per loop

e>

The fastest is the specialized math.sqrt function, which is logical. The ** operator is slightly slower, and the pow function is the slowest, which, in general, has been known for a long time. It makes sense to use the pow function only with the third argument (the remainder modulo), then its use may be justified.
Let's also check if these options produce the same results. At least on some list of natural numbers.:

import mathn =hn = 10000y1 = [x ** 0.5 class="">0.5  for x in range(n)]y2 = [pow(x, 0./span>(x, 0s="">0.5) for x in  range>range(n)]y3 = [math.sqrt(x) for x in range(n)]print(y1 == y2 == y3, sum(y1) == sum(y2) == sum(y3))

)

Output:

True True

You can also look at what code these variants are translated into and make sure, that the code is different:

dis.dis('x ** 0.5') 1an>           0 LOAD_NAME                0 (x)              2 LOAD_CONST               0 (0.5)              4 BINARY_POWER              6 RETU_VALUEdis.dis('pow(x, 0.5)')  1an>           0 LOAD_NAME                0 (pow)              2 LOAD_NAME                1 (x)              4 LOAD_CONST               0 (0.5)              6 CALL_FUNCTION            2              8 RETU_VALUEdis.dis('math.sqrt(x)')   RETU_VALUEdis.dis('math.sqrt(x)')  1           0 LOAD_NAME                0 (math)              2 LOAD_METHOD              1 (sqrt)              4 LOAD_NAME                2 (x)              6 CALL_METHOD              1              8 RETU_VALUE

8 RETU_VALUE

If speed is important, then you probably need to use a specially tailored math.sqrt function to extract the root. In general, it is easier to use the ** operator for all occasions. And in the case when we are interested in the option of exponentiation and further taking the remainder modulo, the pow function is best.

P.S. And, of course, if you need to get many roots at once, it is better to use a vectorized Numpy is for this. Using vectorized functions for the range/sqrt/sum task gives a 50-fold acceleration:

import mathimport numpy as npn ="">as npn = 1000000%timeit sum_py = sum(math.sqrt(n>(math.sqrt(x) for x in range(n))%timeit sum_nu = np.sum< class="">sum(np.sqrt(np.arange(n)))

Output:

10 loops, best of 5: 157 ms per loop100 loops, best of 5: 3.43 ms per loop

an> ms per loop

Checking that the result is consistent (in this case - approximately):

import mathimport numpy as npn ="">as npn = 100"">1000000sum_py = sum(math.sqrt(n>(math.sqrt(x) for x in range(n))sum_nu = np.sum(np.sqrt(np.arange(n)))print(sum_py, abs(sum_py-sum_nu))

e>

Output:

666666166.4588418588418 1.9550323486328125e-05r                                    

Latest

Similar