6

I'm trying to create prime number generator in one-line of Python just as a fun exercise.

The following code works as expected, but it is too slow:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)])
for i in primes(10):
   print i,

So I I tried to do it by only checking up to the square-root of j and k:

primes = lambda q: (i for i in xrange(1,q) if i not in [j*k for j in xrange(1,int(round(math.sqrt(i)+1))) for k in xrange(1,int(round(math.sqrt(i)+1)))])
for i in primes(10):
   print i,

But it outputs: 2 3 5 6 7 8

So there must be something wrong with my indices j and k, but I haven't got a clue.

Zero Piraeus
  • 47,176
  • 24
  • 135
  • 148
Jeffrey Greenham
  • 1,252
  • 5
  • 15
  • 33
  • If you can live with a non-oneliner, there is this question: http://stackoverflow.com/questions/567222/simple-prime-generator-in-python – Andy May 17 '12 at 16:44
  • possible duplicate of [Python- Sieve of Eratosthenes- Compact Python](http://stackoverflow.com/questions/6687296/python-sieve-of-eratosthenes-compact-python) – ninjagecko May 17 '12 at 16:52
  • 2
    I was able to do it in two lines: http://stackoverflow.com/a/9302299/5987 – Mark Ransom May 17 '12 at 16:54
  • You could always take advantage of the fact that you CAN refer to the list currently being built inside of a list comprehension by using the expression `locals['_[1]']`. – APerson Apr 18 '13 at 12:39
  • This is the 3rd question I saw today that asked for an one-liner. Unless you’re Linus Torvalds, and designing Linux, I don’t understand the fad for cramming multiple LOC into a single line. – Abhijit Sarkar Jan 09 '20 at 23:02

11 Answers11

13

That's not the Sieve of Eratosthenes, even though it looks like it is. It is in fact much worse. The Sieve is the best algorithm for finding primes.

See http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

edit: I've modified https://stackoverflow.com/a/9302299/711085 to be a one-liner (originally it was not the real Sieve, but now it is... probably...):

reduce( (lambda r,x: r-set(range(x**2,N,x)) if (x in r) else r), 
        range(2,N), set(range(2,N)))

Demo:

>>> primesUpTo(N): lambda N: reduce(...)
>>> primesUpTo(30)
{2, 3, 5, 7, 11, 13, 17, 19}

Sadly I think that while this would be efficient in a functional programming language, it might not be as efficient in python due to non-persistent (shared-state and immutable) data structures, and any sieve in python would need to use mutation to achieve comparable performance. We can still cram it into a one-liner if we desperately wanted to. But first...

Normal sieve:

>>> N = 100
>>> table = list(range(N))
>>> for i in range(2,int(N**0.5)+1):
...     if table[i]:
...         for mult in range(i**2,N,i):
...             table[mult] = False
... 
>>> primes = [p for p in table if p][1:]
>>> primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

We can now define and call anonymous functions on the same line, as well as the hack of [...].__setitem__ to do inline mutation, and the hack of ... and foo to evaluate ... while returning foo:

>>> primesUpTo = lambda N: (lambda table: [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] for i in range(2,int(N**0.5)+1) if table[i]] and [p for p in table if p][1:])(list(range(N)))
>>> primesUpTo(30)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Proceed to cringe in horror, the one-liner expanded (oddly beautiful because you could almost directly translate the control flow, yet a terrible abuse of everything):

lambda N:
    (lambda table: 
        [[table.__setitem__(mult,False) for mult in range(i**2,N,i)] 
            for i in range(2,int(N**0.5)+1) if table[i]] 
        and [p for p in table if p][1:]
    )(list(range(N)))

This one-liner mutating version gave up at around 108 on my machine, while the original mutating version gave up at around 109, running out of memory (oddly).

The original reduce version gave up at 107. So perhaps it is not that inefficient after all (at least for numbers you can deal with on your computer).

edit2 It seems you can abuse side-effects more concisely as:

reduce( (lambda r,x: (r.difference_update(range(x**2,N,x)) or r)
                     if (x in r) else r), 
        range(2,N), set(range(2,N)))

It gives up at around 108, the same as the one-liner mutating version.

edit3: This runs at O(N) empirical complexity, whereas without the difference_update it ran at O(n^2.2) complexity.

Limiting the range that is reduced over, to the sqrt of the upper limit, and working with odds only, both result in additional speed-ups (2x and 1.6x correspondingly):

reduce( (lambda r,x: (r.difference_update(range(x*x,N,2*x)) or r)
                     if (x in r) else r), 
        range(3, int((N+1)**0.5+1), 2),
        set([2] + range(3,N,2)))
Community
  • 1
  • 1
ninjagecko
  • 77,349
  • 22
  • 129
  • 137
  • True, but this doesn't address the bug in his one-liner. – David Robinson May 17 '12 at 16:52
  • @DavidRobinson: he seemed explicitly a lot more concerned about the speed of his answer, which is directly related to the algorithm. Lookalike algorithms will begin to feel slow at maybe 10000+, and fail at 1000000+. Anyway, I've since edited my answer to give a real one-liner. – ninjagecko May 17 '12 at 17:18
  • 1
    (if anyone is wondering why I keep editing/unediting, it's because it's extremely difficult to tell if an unorthodox implementation of the Sieve actually has a running time of http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Algorithm_complexity ; still even now making sure... sadly I think that while this would be efficient in a functional programming language, it is not efficient in python due to non-shared data structures, and any sieve in python would need to use mutation) – ninjagecko May 17 '12 at 17:35
  • Actually better sieves exist for finding primes. I believe that the current champ is the sieve of Atkins. But Eratosthenes is good enough for virtually anything that you want. – btilly May 17 '12 at 18:19
  • After looking at this and my original two-liner, I don't think they are strictly implementing the Sieve since they remove multiples of numbers that have previously been eliminated as non-prime. I still congratulate you for a reasonable one-line prime number generator. – Mark Ransom May 17 '12 at 19:52
  • @MarkRansom: that's what I originally thought I noticed too (which caused me to flipflop edits back and forth). I think that even the regular sieve of Eratosthenes (and related sieves) have this issue. Nevertheless there is a significant optimization you can do which will avoid lots of re-crossing-out, though possibly not that much: you can cross off the numbers `range(i**2,N,i)` rather than `range(i*2,N,i)`, since i,2*i,3*i,...,(i-1)*i have already been eliminated. – ninjagecko May 17 '12 at 20:19
  • Yes, I discovered that optimization and considered updating my two-liner but ultimately decided the question was stale and didn't warrant an edit. – Mark Ransom May 17 '12 at 21:03
  • 1
    @MarkRansom no, that is what the true Sieve of Eratosthenes does, it *does* remove multiples *several times* - once for each of the number's prime factors. That's because it doesn't "remove" them, when working with arrays - it *marks* them off. To remove an entry from array would break up the random access which is the key to sieve's efficiency - each *mark-off* takes O(1) time. With sets, it is probably O(log(size-of-set)), so worse a bit. And the improvement of crossing-off `range(i**2,N,2*i)` does warrant an edit, IMHO (esp. w/ sets). :) (notice, it's `2*i` when working with odds only). – Will Ness May 18 '12 at 12:12
  • @ninjagecko (your last comment) you mean `2i, 3i ... (i-1)*i` are eliminated. `i` better stay put. :) About that *optimization*, it *should* pay off handsomely when working with sets, *if* access complexity is not O(1) but rather O(log(size-of-set)). Then you do want to make as few crossings-off as possible. So start without evens and work with odds only: `reduce((lambda r,x: r-set(range(x*x,N,2*x))), range(3,int((N+1)**0.5+1),2), set([2]+range(3,N,2)))`. – Will Ness May 18 '12 at 12:20
  • @WillNess, What I meant is that a true Sieve of Eratosthenes only marks off multiples of the primes, not the multiples of every odd number. There are a goodly number more odd numbers than primes, making it unnecessarily slow. – Mark Ransom May 18 '12 at 12:28
  • ah, yes, of course. :) even with primes, many/some composites will be eliminated multiple times. – Will Ness May 18 '12 at 12:31
  • @MarkRansom: ack, I thought I had considered that, which was one of the reasons I suddenly removed the answer and put it back in. Guess my intuition was right. Thank you for pointing that out. Edited. – ninjagecko May 18 '12 at 13:38
  • Try using `reduce( ..., range(2,int((N+1)**0.5+1)), ...)`, it should make for a *dramatic* improvement in speed. For some reason, trying to limit the work to odds only makes it run slower, **but**, limiting the range to a sqrt is still very much worth it, making [the code which runs](http://ideone.com/rQjKr) at **O(n^1.5)** instead of O(n^2.2) [with the full range](http://ideone.com/vZB5z), so that e.g. for primes up to 100,000 the former runs 62 times faster than the latter. – Will Ness May 25 '12 at 21:44
  • interesting, using difference_update brings down empirical complexity to below O(n) !! Still, limiting the range that is reduced over, to sqrt, makes for code that runs [twice faster](http://ideone.com/Qyzjp). But now, working with odds only pays off - it's [60% faster yet](http://ideone.com/gl1h9). – Will Ness May 25 '12 at 22:18
3

You can't check products of numbers only up to the square root to test for a prime. Look at 8- the square root of 8 is 2.8, so it will never try 4 * 2. (Indeed, the only numbers that wouldn't be seen as primes are square numbers).

ETA: Instead of trying all possible combinations of j and k, why not check if i is divisible by each j (using i % j == 0) up to the square root of j? This both takes less code and is much more efficient (though it is still not nearly as efficient as the Sieve of Eratosthenes).

David Robinson
  • 71,331
  • 13
  • 150
  • 174
3

Here's what you wanted:

def primes (q) :
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,i)])
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i) for k in xrange(1,j+1)])
 # return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,j+1)])

 return (i for i in xrange(2,q) if i not in [j*k for j in xrange(1,i/2+1) for k in xrange(1,min(j+1,i/j+1))])

In Haskell, the ranges are inclusive, so primes(542) is

[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],     k<-[1..n-1]]]  --  25.66s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n-1],     k<-[1..j]]]    --  15.30s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..j]]]    --   6.00s
                                                                      --   0.79s
[n | n<-[2..541], not $ elem n [j*k | j<-[1..n`div`2], k<-[1..min j (n`div`j)]]] 

And actually, 1*x == x so 1 isn't needed as a multiplier, thus it should be

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..min j (n`div`j)]]] 

which takes only 0.59 seconds. Or, in Python,

def primes (q) :
 return (i for i in xrange(2,q) if i not in [j*k for j in xrange(2,i/2+1) for k in xrange(2,min(j+1,i/j+1))])

update: for some reason, min j ... doesn't make much of a difference, in Haskell at least. So the expression becomes simply

[n | n<-[2..541], not $ elem n [j*k | j<-[2..n`div`2], k<-[2..n`div`j]]] 
Will Ness
  • 62,652
  • 8
  • 86
  • 167
3

How about:

def primes(x):
  return [i for i in range(2,x) if 0 not in [i%j for j in range(2,i)]]
Questions
  • 41
  • 1
1

using comprehensions

[x for x in range(4, 1000) if all(x % y != 0 for y in range(2, int(math.sqrt(x)) + 1))]
Amit
  • 11
  • 1
0
def isPrime(n):
    return all(n % d for d in range(2, int(n**.5)+1))

primes = set(n for n in range(2, 100) if isPrime(n))

This can be compressed to one line if needed:

primes = set(n for n in range(2, 100) if all(n % d for d in range(2,int(n**.5)+1)))

Where all() ensures that none of the values are False (or 0); in this case that ensures that none of the numbers below the number we're testing are factors, in which case the number is prime.

We check all factors up to the square root of the number for efficiency: If no numbers below sqrt(n) are factors, no numbers above sqrt(n) are factors, because factors always come in pairs.

Zaz
  • 39,637
  • 10
  • 70
  • 92
0

Although there are other great answers here, I wanted to include my version of it.

n = 1000
primes = [candidate for candidate in range(1, n) if candidate not in [x * mult for x in range(2,n // 2 + 1) for mult in range(2,n//x)]]

This has a running time of O(n**2).

mehrdadjg
  • 376
  • 1
  • 11
0

here is a one line code to do it:

print((lambda n:[i for i in range(2, n) if ["A7A" for j in range(2, i) if i%j==0]==[]])(100))
Ahmed4end
  • 206
  • 1
  • 5
  • 13
0

Here is another way which is not a one-liner but is more efficient. The positive side is that every time you only check for previously found prime numbers rather than looking into all values in range(n):

def prime(n):
    pm_list = [2]
    for i in range(3,n+1):
        pm_arr = np.array(pm_list)
        if any(i // pm_arr == i / pm_arr) == False:
            pm_list.append(i)
    return pm_list
DevMP
  • 1
0

Here is a oneliner based on the sieve of Eratosthenes.

primes = lambda N: (N>1)*[2]+[p for s in [[1]*(N+1)] for p in range(3,N+1,2) if s[p] and not s.__setitem__(slice(p,None,p),N//p*[0])]

It generates primes up to 1,000,000 in 0.097 sec on my laptop.

For primes up to 10^8 : 11.6 seconds,

For primes up to 10^9 : 169.3 seconds (2.8 minutes)

For primes up to 10^10 : seems to run out of memory (IDLE shell quits)

Alain T.
  • 24,524
  • 2
  • 27
  • 43
-1

My code is (for numbers in range(2,50)):

import operator

[len(x) for x in list(map(lambda x: [operator.mod(len(range(1,x)), z) for z in range(1,x)], [item for item in range(2,50)])) if x.count(0) == 2]