On Thu, Jul 11, 2013 at 1:47 AM, bas <blswink...@gmail.com> wrote:
> On Wednesday, July 10, 2013 5:12:19 PM UTC+2, Chris Angelico wrote:
>> Well, that does answer the question. Unfortunately the use of lambda
>> there has a severe performance cost [ ...]
> If you care about speed, you might want to check the heapq module. Removing 
> the smallest item and inserting a new item in a heap both cost O(log(N)) 
> time, while finding the minimum in a dictionary requires iterating over the 
> whole dictionary, which cost O(N) time.

Ehh, speed isn't the ultimate. I was just trying to avoid something
that worked out ridiculously slow (a Python function call IS quite
slow). I haven't profiled the code to find out where the bulk of the
time is spent, but switching in the lambda-based version doubled total
run time, so I didn't like it :)

> (untested)
> #before loop
> from heapq import *
> primes = [(2,2)] #heap of tuples (multiple, prime). start with 1 item, so no 
> need for heapify
>
> #during loop
> smallest, prm = heappop(primes)
> heappush(primes, (smallest+prm, prm))
>
> #when new prime found
> heappush(primes, (i+i, i))

Ahh, that's the bit I should have thought of! Of course.

My original thought experiment had involved basically a really long
list, like the classic Sieve but getting longer as time moves on, with
composites replaced by None and primes with their next-markers, which
I then collapsed to a dict. Always I was thinking in terms of indexing
with the prime to get its next composite. Here's the code involving
heapq:

# -- start --
def primes():
        """Generate an infinite series of prime numbers."""
        from heapq import heappush,heappop
        i=2
        yield 2
        prime=[(2,2)] # Heap
        while True:
                smallest, prm = heappop(prime)
                heappush(prime, (smallest+prm, prm))
                while i<smallest:
                        yield i
                        heappush(prime, (i+i, i))
                        i+=1
                if i==smallest: i+=1

gen=primes()
print([next(gen) for i in range(10)])
for i in range(1000):
        next(gen) # Star Trek?
print("The next prime number is:",next(gen))
# -- end --

And that's significantly shorter, clearer, AND faster than the original. Thanks!

>> > Still trying to figure out your algorithm ...
>> It's pretty simple.  [...]
> I understand what you are trying, but it is not immediately clear to me that 
> it works correctly if for example a smallest factor appears twice in the 
> list. I don't have time for it now, but it for sure can be simplified. The 
> while loop, for example, can be replaced by an if, since it will never 
> execute more than once (since if i is prime > 2, i+1 will be divisible by 2)

Ah, good point. Again, I originally was starting from 1, so the while
loop was necessary to catch 2 and 3 in the first run. When I changed
it to start at 2 and explicitly yield it first, I didn't notice to
change that.

It works correctly with the smallest multiple appearing twice because
it won't yield primes until the smallest value is higher than the
current next-prime. So when it's just yielded 11, for instance, both
the 2 and 3 slots hold 12; advancing one of those does nothing,
advancing the other allows the bottom loop to notice that 13 is now
lower than the minimum value (which will then be 14).

ChrisA
-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to