1

I've written the following function for estimating the definite integral of a function with Simpson's Rule:

def fnInt(func, a, b):
    if callable(func) and type(a) in [float] and type(b) in [float]:
        if a > b:
            return -1 * fnInt(func, b, a)
        else:
            y1 = nDeriv(func)
            y2 = nDeriv(y1)
            y3 = nDeriv(y2)
            y4 = nDeriv(y3)
            f = lambda t: abs(y4(t))
            k = f(max(f, a, b))
            n = ((1 / 0.00001) * k * (b - a) ** 5 / 180) ** 0.25
            if n > 0:
                n = math.ceil(n) if math.ceil(n) % 2 == 0 else math.ceil(n) + 1
            else:
                n = 2
            x = (b - a) / n
            ans = 0
            for i in range(int((n - 4) / 2 + 1)):
                ans += (x / 3) * (4 * func(a + x * (2 * i + 1)) + 2 * func(a + x * (2 * i + 2)))
            ans += (x / 3) * (func(a) + 4 * func(a + x * (n - 1)) + func(b))
            return ans
    else:
        raise TypeError('Data Type Error')

It seems, however, that whenever I try to use this function, it takes forever to produce an output. Is there a way that I can rewrite this code in order to take up less time?

  • 1
    Have you ever tried profiling, to see what is taking most of the time? – Paul Rooney Oct 09 '18 at 03:21
  • Hi Michael, you might want to put this Q into the following: https://codereview.stackexchange.com/ – IronKirby Oct 09 '18 at 03:33
  • If you are doing this for a "serious" purpose rather than to teach yourself Python, you should use [SciPy](https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.simps.html) instead of writing it yourself. Their implementation uses NumPy arrays and will be much faster. – Kevin Oct 09 '18 at 07:05

1 Answers1

0

As one of the comments mentioned, profiling the code will show you the slowdowns. Perhaps nDeriv is slow. If you don't have a profiling tool, you can put time() calls around each section of code and print the results. More info here: Measure time elapsed in Python?

So, if the slowdown ends up being in your for loop, here are a few things you can try:

  1. Python might be computing the loop condition every iteration:

    for i in range(int((n - 4) / 2 + 1)):

calculate int((n - 4) / 2 + 1) once before the loop.

  1. Don't recalculate stuff inside the loops that doesn't change. For example, x / 3 is going to be recalculated every loop iteration, but it never changes. Do it before the loop starts.

Likewise, you're doing 2 * i twice every loop iteration.

  1. Addition is faster than multiplication. The func arguments could be re-written as:

    xi = x * i a1 = a + xi + xi + x a2 = a1 + x

and then taking it a step further, you could also re-do xi as an accumulator. That is, start with x = 0, then every iteration simply x += x

  1. This is probably obvious, but if func() is difficult to calculate, this function will be exponentially slow.

Python may be doing a lot of simpler optimizations for you, so these may not help, but just wanted to share some ideas.

Dale
  • 534
  • 4
  • 13