David S Jackson
David S Jackson

Reputation: 36

Python3, map-function

I'm trying to optimize this piece of code for speed only (and correctness, of course) in python3:

from math import log
from timeit import Timer

def groffle_slow(mass, density):
    total = 0.0
    for i in range(10000):
        masslog = log(mass * density)
        total += masslog/(i+1)
    return total

I've been amazed at how much map speeds things up, so...

def groffle_faster(mass, density):
    total = 0.0
    masslog = log(mass * density)
    return map(sum, (masslog/(i+1) for i in range(10000)))

Looking at the difference in time of execution, there's no comparison. groffle_faster() is waaay faster, but it's returning a map object. The map object should contain the sum as a float.

Anyway I can get the float out of the map object?

Thanks!

Upvotes: 0

Views: 544

Answers (1)

DSM
DSM

Reputation: 353604

It's waaay faster because it's not doing anything; and if it were, it wouldn't work.

>>> mass = 1.2
>>> density = 2.3
>>> masslog = math.log(mass * density)
>>> map(sum, (masslog/(i+1) for i in range(10000)))
<map object at 0x7feccaf1fc18>

This map object is a lazy object which will yield the results of the function sum applied to each element of the iterable, which in this case is the generator expression (masslog/(i+1) for i in range(10000)). No calculations have been done.

But this doesn't make much sense here anyway, because you're trying to apply the sum function to each element individually, and so:

>>> list(map(sum, (masslog/(i+1) for i in range(10000))))
Traceback (most recent call last):
  File "<ipython-input-13-c0f9c805843a>", line 1, in <module>
    list(map(sum, (masslog/(i+1) for i in range(10000))))
TypeError: 'float' object is not iterable

What you really want is simply

>>> sum(masslog/(i+1) for i in range(10000))
9.936677928893602

which would give

>>> %timeit groffle_slow(1.5, 2.5)
100 loops, best of 3: 5.08 ms per loop
>>> %timeit groffle_fast(1.5, 2.5)
100 loops, best of 3: 3.02 ms per loop

But since sum(1/(i+1) for i in range(10000)) is a fixed number, I'm not sure why you don't simply use something like

>>> def groffle_O1(mass, density):
...     MSUM = 9.787606036044345
...     return log(mass*density) * MSUM
... 
>>> %timeit groffle_O1(1.5, 2.5)
1000000 loops, best of 3: 424 ns per loop

But you haven't specified what constraints you're really operating under, so it's hard to know what your real problem is.

Upvotes: 3

Related Questions