You could use a few tricks to implement the two sequences more efficiently, but the short version of my answer is that most significant performance improvements you could make involve some relatively advanced math, and the smaller improvements do more to improve your code's readability than its performance.
Useful improvements to prime
If you keep a list of the primes you find, you only need to check if those divide the new numbers you are checking, rather than checking every number up to the number you are looking at.
You could also skip over even numbers in the outer loop (use range(3, max, 2)
), thus avoiding checking even numbers that you can be sure aren't prime (you would need to add a special case for 2).
The inner loop (j
) can stop at i/2
, because no number can be evenly divided by a number more than half its size. Similarly, you can stop the loop at when you pass the square root of n
, but you would have to implement that by squaring the factors because sqrt
is limited by the inaccuracy of floating-point numbers.
Using all of these suggestions, the code might look a little like this:
def nth_prime(n):
if n == 1:
return 2
primes = [2]
for candidate_prime in range(3, MAX, 2):
for p in primes:
if (p ** 2 > candidate_prime):
break # candidate is prime
if (candidate_prime % p == 0)
break # p divides candidate; candidate is not prime
if no primes divided candidate_prime:
primes.append(candidate_prime)
if len(primes) == n:
return candidate_prime
Additional optimizations for determining whether a number is prime are discussed on the Wikipedia page on the subject.
These improvements will only start to have noticeable effects when you start looking at very large primes, so you might also want to use itertools.count
to look at all numbers instead of stopping at 100.
(If you really want to stop at 100, you could also just make a list of the prime numbers up to 100 and index that for maximum efficiency.)
Links to mathematical solutions
To really improve efficiency, this answer suggests the solution in this blog post, but this is probably overkill unless you really want to be able to calculate very large fibonacci numbers very fast (I can't tell that there's a delay in your function until somewhere far above n=10000
).
This question goes into depth about how to find the nth prime number, but the final point is important to note:
tl;dr: Finding the nth prime can be efficiently done, but the more efficient you want it, the more mathematics is involved.
Other suggestions
The following suggestions aren't really about efficiency (if they change the efficiency of your code, the difference will be immeasurably small), but they might make your code a little cleaner.
In the loop you're using for the Fibonacci sequence, you can just write t1, t2 = t2, t1 + t2
to update both values in one line.
When creating the nth Fibonacci number, you can just loop over range(n)
; there's no need to adjust the ends of the loop. And if you're not using the index i
in that loop, Python convention is call it _
, so you end up with for _ in range(n)
.
Using Python's less-than-well-known for
/else
feature might let you eliminate the flag variable. If you put an else
block after a loop in Python, it will run only if you do not break
out of the loop, which allows you to avoid flag variables.
for i in some_list:
if condition(i):
break
else:
do_this_if_condition_was_never_true()