Search code examples
pythonfunctionintegral

Calculating Integrals: One-liner solution?


I was working on a program to find the integral of a function, where the user specifies the amount of rectangles, the start, and the stop.

NOTE: I am using left-end points of the rectangles.

I have the function working perfectly (at least, it seems to be perfect). However, I wanted to see if I could write a one-liner for it, but not sure how because I'm using eval(). Here is my original code:

def integral(function, n=1000, start=0, stop=100):
    """Returns integral of function from start to stop with 'n' rectangles"""
    increment, rectangles, x = float((stop - start)) / n, [], start
    while x <= stop:
        num = eval(function)
        rectangles.append(num)
        if x >= stop: break
        x += increment
    return increment * sum(rectangles)

This works fine:

>>> integral('x**2')
333833.4999999991

The actual answer is 1000000/3, so my function gives a pretty nice estimate (for only 1000 rectangles).

My attempt at a one-liner:

def integral2(function, n=1000, start=0, stop=100): rectangles = [(float(x) / n) for x in range(start*n, (stop*n)+1)]; return (float((stop-start))/n) * sum([eval(function) for x in rectangles])

However, this isn't a truly a one-liner since I'm using a semi-colon. Also, it's a bit slower (takes a few seconds longer, which is pretty significant) and gives the wrong answer:

>>> integral2('x**2')
33333833.334999967

So, is it possible to use a one-liner solution for this function? I wasn't sure how to implement eval() and float(x)/n in the same list comprehension. float(x)/n achieves a virtual 'step' in the range function.

Thanks!


Solution

  • def integral2(function, n=1000, start=0, stop=100): return (float(1)/n) * sum([eval(function) for x in [(float(x) / n) for x in range(start*n, (stop*n)+1)]])
    

    Note that there is a big difference between integral and integral2: integral2 makes (stop*n)+1-(start*n) rectangles, while integral only makes n rectangles.


    In [64]: integral('x**2')
    Out[64]: 333833.4999999991
    In [68]: integral2('x**2')
    Out[68]: 333338.33334999956
    

    In [69]: %timeit integral2('x**2')
    1 loops, best of 3: 704 ms per loop
    
    In [70]: %timeit integral('x**2')
    100 loops, best of 3: 7.32 ms per loop
    

    Perhaps a more comparable translation of integral would be:

    def integral3(function, n=1000, start=0, stop=100): return (float(stop-start)/n) * sum([eval(function) for x in [start+(i*float(stop-start)/n) for i in range(n)]])
    
    In [77]: %timeit integral3('x**2')
    100 loops, best of 3: 7.1 ms per loop
    

    Of course, it should go with say that there is no purpose for making this a one-liner other than (perverse?) amusement :)