Skip to content

Commit 0b78195

Browse files
approach for nth prime (#3496)
1 parent 8ebf557 commit 0b78195

File tree

6 files changed

+197
-0
lines changed

6 files changed

+197
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"introduction": {
3+
"authors": ["safwansamsudeen"]
4+
},
5+
"approaches": [
6+
{
7+
"uuid": "c97a3f8e-a97d-4e45-b44f-128bcffb2d3a",
8+
"slug": "generator-fun",
9+
"title": "Generator Fun",
10+
"blurb": "Utilize Python library and generators",
11+
"authors": ["safwansamsudeen"]
12+
},
13+
{
14+
"uuid": "e989fdd2-3f39-4195-9d7c-120a6d6376b6",
15+
"slug": "tracking",
16+
"title": "Tracking",
17+
"blurb": "Track previous prime values and return the nth one",
18+
"authors": ["safwansamsudeen"]
19+
}
20+
]
21+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Generator Fun
2+
The key of this approach is to not store the elements you do not need.
3+
4+
This is a code representation:
5+
```python
6+
from itertools import islice, count
7+
8+
def prime(number):
9+
if number == 0:
10+
raise ValueError('there is no zeroth prime')
11+
gen = islice(filter(lambda counter: all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1)), count(2)), number)
12+
for _ in range(number - 1): next(gen)
13+
return next(gen)
14+
```
15+
16+
Let's dissect it! `itertools.count` is like `range` without un upper bound - calling it returns a generator, and `for ... in count_obj` will result in an infinite loop.
17+
18+
Using a lambda expression, we `filter` out any numbers above two that are prime.
19+
Doesn't this result in an infinite loop?
20+
No - `filter` _also_ returns a generator object (which are [evaluated lazily][generator]), so while it's too will produce values infinitely if evaluated, it doesn't hang to program at the time of instantiation.
21+
22+
`itertools.islice` takes in a generator object and an end count, returning a generator object which _only evalutes until that end count_.
23+
24+
The next line exhausts all the values in the generator except the end, and we finally return the last element.
25+
26+
We can utilize the `functools.cache` decorator for greater speeds at higher values of `number`, so we take it out. The added bonus is that a very long line of code is cleant up.
27+
28+
```python
29+
from itertools import islice, count
30+
from functools import cache
31+
32+
@cache
33+
def is_prime(counter):
34+
return all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1))
35+
36+
def prime(number):
37+
if number == 0:
38+
raise ValueError('there is no zeroth prime')
39+
gen = islice(filter(is_prime, count(2)), number)
40+
for _ in range(number - 1): next(gen)
41+
return next(gen)
42+
```
43+
44+
~~~~exercism/note
45+
Note that this that not create a list anywhere, and thus is both memory and time efficient.
46+
~~~~
47+
48+
Read more on `itertools` on the [Python docs][itertools].
49+
50+
[itertools]: https://docs.python.org/3/library/itertools.html
51+
[generator]: https://www.programiz.com/python-programming/generator
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from itertools import islice, count
2+
3+
def prime(number):
4+
gen = islice(filter(lambda n: all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1)), count(2)), number)
5+
for _ in range(number - 1): next(gen)
6+
return next(gen)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Introduction
2+
Nth Prime in Python is a very interesting exercise that can be solved in multiple ways.
3+
4+
## General guidance
5+
Every approach has to a) validate the input, b) calculate the prime numbers until n - 1, and c) return the nth prime number.
6+
7+
As the previous numbers are calculated multiple times, it's advisable to extract that piece of code as a function and use `functools.cache` for greater speeds at higher numbers.
8+
9+
## Approach: Tracking
10+
Using this approach, we manually track the primes/number of primes until the nth prime, after which we quit the loop and return the last number in the list/currently tracked prime.
11+
There are many possible code implementations, such as this one:
12+
```python
13+
def prime(number):
14+
if number == 0:
15+
raise ValueError('there is no zeroth prime')
16+
counter = 2
17+
primes = [2]
18+
while len(primes) < number:
19+
counter += 1
20+
if all(counter % test != 0 for test in primes):
21+
primes.append(counter)
22+
return primes[-1]
23+
```
24+
25+
If you're interested in learning more about this approach (and discover a lesser known Python feature!), go [here][approach-tracking].
26+
27+
## Approach: Generator Fun
28+
A far more idiomatic approach that utilizes Python's powerful standard library is to use `itertools` and generator expression related functions.
29+
30+
```python
31+
from itertools import islice, count
32+
33+
def is_prime(n):
34+
return not any(n % k == 0 for k in range(2, int(n ** 0.5) + 1))
35+
36+
def prime(number):
37+
if number == 0:
38+
raise ValueError('there is no zeroth prime')
39+
gen = islice(filter(is_prime, count(2)), number)
40+
for _ in range(number - 1): next(gen)
41+
return next(gen)
42+
```
43+
If you'd like to understand this approach better, [read more][approach-generator-fun].
44+
45+
[approach-tracking]: https://exercism.org/tracks/python/exercises/nth-prime/approaches/tracking
46+
[approach-generator-fun]: https://exercism.org/tracks/python/exercises/nth-prime/approaches/generator-fun
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Tracking
2+
This approach includes building a list of all the previous primes until it reaches the nth number, after which it quits the loop and return the last number in the list.
3+
```python
4+
def prime(number):
5+
if number == 0:
6+
raise ValueError('there is no zeroth prime')
7+
counter = 2
8+
primes = [2]
9+
while len(primes) < number:
10+
counter += 1
11+
if all(counter % test != 0 for test in range(2, counter)):
12+
primes.append(counter)
13+
return primes[-1]
14+
```
15+
Efficiency can be improved slightly by reducing the factor check to the square root of the number...
16+
```python
17+
...
18+
if all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1)):
19+
...
20+
```
21+
... or even better, checking whether a new number is merely not divisible by any of the primes (in which case it's a prime itself):
22+
```python
23+
...
24+
if all(counter % test != 0 for test in primes):
25+
...
26+
```
27+
Instead of building the list, it's more memory efficient to just save the number of primes found until now, and return the currently stored number when the nth prime is found.
28+
However, this elongates the code.
29+
```python
30+
def prime(number):
31+
if number == 0:
32+
raise ValueError('there is no zeroth prime')
33+
counter = 2
34+
prime_count = 0
35+
while True:
36+
isprime = True
37+
for test in range(2, int(counter ** 0.5) + 1):
38+
if counter % test == 0:
39+
isprime = False
40+
break
41+
if isprime:
42+
prime_count += 1
43+
if prime_count == number:
44+
return counter
45+
counter += 1
46+
```
47+
48+
~~~~exercism/advanced
49+
Tip: you can use `for... else` to make your code more idiomatic here.
50+
```python
51+
...
52+
for test in range(2, int(counter ** 0.5) + 1):
53+
if counter % test == 0:
54+
break
55+
else:
56+
prime_count += 1
57+
if prime_count == number:
58+
return counter
59+
```
60+
The else block is executed if the `for` loop completes normally - that is, without `break`ing.
61+
Read more on [for/else][for-else]
62+
~~~~
63+
64+
65+
[for-else]: https://book.pythontips.com/en/latest/for_-_else.html
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
def prime(number):
2+
counter = 2
3+
primes = [2]
4+
while len(primes) < number:
5+
counter += 1
6+
if all(counter % test != 0 for test in range(2, counter)):
7+
primes.append(counter)
8+
return primes[-1]

0 commit comments

Comments
 (0)