-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1e71b8a
commit 8ad08af
Showing
6 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"introduction": { | ||
"authors": ["safwansamsudeen"] | ||
}, | ||
"approaches": [ | ||
{ | ||
"uuid": "c97a3f8e-a97d-4e45-b44f-128bcffb2d3a", | ||
"slug": "generator-fun", | ||
"title": "Generator Fun", | ||
"blurb": "Utilize Python library and generators", | ||
"authors": ["safwansamsudeen"] | ||
}, | ||
{ | ||
"uuid": "e989fdd2-3f39-4195-9d7c-120a6d6376b6", | ||
"slug": "tracking", | ||
"title": "Tracking", | ||
"blurb": "Track previous prime values and return the nth one", | ||
"authors": ["safwansamsudeen"] | ||
} | ||
] | ||
} |
51 changes: 51 additions & 0 deletions
51
exercises/practice/nth-prime/.approaches/generator-fun/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Generator Fun | ||
The key of this approach is to not store the elements you do not need. | ||
|
||
This is a code representation: | ||
```python | ||
from itertools import islice, count | ||
|
||
def prime(number): | ||
if number == 0: | ||
raise ValueError('there is no zeroth prime') | ||
gen = islice(filter(lambda counter: all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1)), count(2)), number) | ||
for _ in range(number - 1): next(gen) | ||
return next(gen) | ||
``` | ||
|
||
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. | ||
|
||
Using a lambda expression, we `filter` out any numbers above two that are prime. | ||
Doesn't this result in an infinite loop? | ||
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. | ||
|
||
`itertools.islice` takes in a generator object and an end count, returning a generator object which _only evalutes until that end count_. | ||
|
||
The next line exhausts all the values in the generator except the end, and we finally return the last element. | ||
|
||
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. | ||
|
||
```python | ||
from itertools import islice, count | ||
from functools import cache | ||
|
||
@cache | ||
def is_prime(counter): | ||
return all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1)) | ||
|
||
def prime(number): | ||
if number == 0: | ||
raise ValueError('there is no zeroth prime') | ||
gen = islice(filter(is_prime, count(2)), number) | ||
for _ in range(number - 1): next(gen) | ||
return next(gen) | ||
``` | ||
|
||
~~~~exercism/note | ||
Note that this that not create a list anywhere, and thus is both memory and time efficient. | ||
~~~~ | ||
|
||
Read more on `itertools` on the [Python docs][itertools]. | ||
|
||
[itertools]: https://docs.python.org/3/library/itertools.html | ||
[generator]: https://www.programiz.com/python-programming/generator |
6 changes: 6 additions & 0 deletions
6
exercises/practice/nth-prime/.approaches/generator-fun/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from itertools import islice, count | ||
|
||
def prime(number): | ||
gen = islice(filter(lambda n: all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1)), count(2)), number) | ||
for _ in range(number - 1): next(gen) | ||
return next(gen) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Introduction | ||
Nth Prime in Python is a very interesting exercise that can be solved in multiple ways. | ||
|
||
## General guidance | ||
Every approach has to a) validate the input, b) calculate the prime numbers until n - 1, and c) return the nth prime number. | ||
|
||
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. | ||
|
||
## Approach: Tracking | ||
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. | ||
There are many possible code implementations, such as this one: | ||
```python | ||
def prime(number): | ||
if number == 0: | ||
raise ValueError('there is no zeroth prime') | ||
counter = 2 | ||
primes = [2] | ||
while len(primes) < number: | ||
counter += 1 | ||
if all(counter % test != 0 for test in primes): | ||
primes.append(counter) | ||
return primes[-1] | ||
``` | ||
|
||
If you're interested in learning more about this approach (and discover a lesser known Python feature!), go [here][approach-tracking]. | ||
|
||
## Approach: Generator Fun | ||
A far more idiomatic approach that utilizes Python's powerful standard library is to use `itertools` and generator expression related functions. | ||
|
||
```python | ||
from itertools import islice, count | ||
|
||
def is_prime(n): | ||
return not any(n % k == 0 for k in range(2, int(n ** 0.5) + 1)) | ||
|
||
def prime(number): | ||
if number == 0: | ||
raise ValueError('there is no zeroth prime') | ||
gen = islice(filter(is_prime, count(2)), number) | ||
for _ in range(number - 1): next(gen) | ||
return next(gen) | ||
``` | ||
If you'd like to understand this approach better, [read more][approach-generator-fun]. | ||
|
||
[approach-tracking]: https://exercism.org/tracks/python/exercises/nth-prime/approaches/tracking | ||
[approach-generator-fun]: https://exercism.org/tracks/python/exercises/nth-prime/approaches/generator-fun |
65 changes: 65 additions & 0 deletions
65
exercises/practice/nth-prime/.approaches/tracking/content.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Tracking | ||
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. | ||
```python | ||
def prime(number): | ||
if number == 0: | ||
raise ValueError('there is no zeroth prime') | ||
counter = 2 | ||
primes = [2] | ||
while len(primes) < number: | ||
counter += 1 | ||
if all(counter % test != 0 for test in range(2, counter)): | ||
primes.append(counter) | ||
return primes[-1] | ||
``` | ||
Efficiency can be improved slightly by reducing the factor check to the square root of the number... | ||
```python | ||
... | ||
if all(counter % test != 0 for test in range(2, int(counter ** 0.5) + 1)): | ||
... | ||
``` | ||
... 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): | ||
```python | ||
... | ||
if all(counter % test != 0 for test in primes): | ||
... | ||
``` | ||
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. | ||
However, this elongates the code. | ||
```python | ||
def prime(number): | ||
if number == 0: | ||
raise ValueError('there is no zeroth prime') | ||
counter = 2 | ||
prime_count = 0 | ||
while True: | ||
isprime = True | ||
for test in range(2, int(counter ** 0.5) + 1): | ||
if counter % test == 0: | ||
isprime = False | ||
break | ||
if isprime: | ||
prime_count += 1 | ||
if prime_count == number: | ||
return counter | ||
counter += 1 | ||
``` | ||
|
||
~~~~exercism/advanced | ||
Tip: you can use `for... else` to make your code more idiomatic here. | ||
```python | ||
... | ||
for test in range(2, int(counter ** 0.5) + 1): | ||
if counter % test == 0: | ||
break | ||
else: | ||
prime_count += 1 | ||
if prime_count == number: | ||
return counter | ||
``` | ||
The else block is executed if the `for` loop completes normally - that is, without `break`ing. | ||
Read more on [for/else][for-else] | ||
~~~~ | ||
|
||
|
||
[for-else]: https://book.pythontips.com/en/latest/for_-_else.html |
8 changes: 8 additions & 0 deletions
8
exercises/practice/nth-prime/.approaches/tracking/snippet.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
def prime(number): | ||
counter = 2 | ||
primes = [2] | ||
while len(primes) < number: | ||
counter += 1 | ||
if all(counter % test != 0 for test in range(2, counter)): | ||
primes.append(counter) | ||
return primes[-1] |