0

The code below is for a sum of the defined function from the lower limit 2 and I vary the upper limit of the sum because I want to find out where the sum converges. I found that the bigger the upper limit the slower the code runs. How do I make this code run fast for any large upper limit value? The code is as follows:

def h(i):
    x = (-1)**(i+1)
    y = 1000000000-(i-1)
    z = (log(i))**20
    return x*y*z

 gx = sum(h(i) for i in range (2, 1000000000+1))
 d_gx = gx/1000000000
 print(d_gx)
guidot
  • 5,095
  • 2
  • 25
  • 37
Thando
  • 23
  • 4
  • First you could avoid multiplication in `h(i)` by using `x = (i%2 == 0) ? -1 : 1`. I suppose that will not improve a lot performances but that's a little thing. Also, what does take time ? Summing or computing `h(i)` for large values of `i` ? – Maxouille Mar 30 '21 at 11:57
  • Posting actual performance would also help. You could use the `%timeit` magic in an IPython console for instance. – deponovo Mar 30 '21 at 12:00
  • If you are not able, to perform some formula optimization, nothing will help here; you have a problem with a runtime directly proportional to n. While you may reduce the constant factor, for twelve-digit numbers the effect is limited. – guidot Mar 30 '21 at 12:01
  • 1
    Numba will help you a lot here. Especially with vectorization. That loop in the range is gonna be slow. – erip Mar 30 '21 at 12:01

1 Answers1

3

Numba is a python library for just-in-time optimization of pure python code without the need for external compilation steps.

The two important functions I'll introduce here are numba.njit and numba.vectorize which are both decorators. njit optimizes an arbitrary pure function and vectorize makes functions operate on both scalars and ndarrays.

In [1]: from numba import vectorize, njit; from math import log

In [2]: def h(i):
   ...:     x = (-1)**(i+1)
   ...:     y = 1000000000-(i-1)
   ...:     z = (log(i))**20
   ...:     return x*y*z
   ...:

In [3]: %timeit sum(h(i) for i in range (2, 1000000+1))
646 ms ± 9.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

As you can see here, the naive implementation of your function takes an average of 646ms on a reduced amount of the input space. We can improve this by jitting your function:

In [4]: jit_h = njit()(h)

In [5]: %timeit sum(jit_h(i) for i in range (2, 1000000+1))

179 ms ± 3.88 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

We've trimmed this down to 179ms, which is a huge improvement over the original 646ms. Because for-loops are slow, we can try to vectorize the operation using numpy arrays as inputs:

In [6]: vectorize_h = vectorize()(h)

In [7]: %timeit sum(vectorize_h(i) for i in range (2, 1000000+1))
657 ms ± 4.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

As expected, vectorizing the input permits passing scalars, but doesn't dramatically improve performance -- in fact, it's a bit slower! What if we operate on an entire numpy array?

In [8]: import numpy as np

In [9]: %timeit sum(vectorize_h(np.arange(2,1000000+1))) 

149 ms ± 1.78 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Finally, what if we replace the sum builtin with numpy ndarray sum?

In [10]: %timeit vectorize_h(np.arange(2,1000000+1)).sum() 
17.2 ms ± 207 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

A further reduction down to 17.2ms -- a huge improvement over the naive implementation.

erip
  • 16,374
  • 11
  • 66
  • 121
  • Thank you so much. With this improved code how do I print the actual value of the sum? – Thando Mar 30 '21 at 21:37
  • `%timeit` accepts an expression, so you just remove that bit and you'll get the answer. – erip Mar 30 '21 at 21:38
  • Thank you so much Erip. I appreciate it. – Thando Mar 31 '21 at 09:47
  • @Thando If this answers your question sufficiently you can accept it by clicking the checkmark. – erip Mar 31 '21 at 11:21
  • I still find that I'm having trouble with large numbers though. It works fast for an upper limit of up to 1million but for any number above that my python and laptop just freezes. I'm not sure whether the problem is with my python or laptop. – Thando Apr 03 '21 at 13:55