Optimalization of the primes finding function - python

After 10 minutes of work I have written a function presented below. It returns a list of all primes lower than an argument. I have used all known for me programing and mathematical tricks in order to make this function as fast as possible. To find all the primes lower than a million it takes about 2 seconds.
Do you see any possibilities to optimize it even further? Any ideas?
def Primes(To):
if To<2:
return []
if To<3:
return [2]
Found=[2]
n=3
LastSqr=0
while n<=To:
k=0
Limit=len(Found)
IsPrime=True
while k<Limit:
if k>=LastSqr:
if Found[k]>pow(n,0.5):
LastSqr=k
break
if n%Found[k]==0:
IsPrime=False
break
k+=1
if IsPrime:
Found.append(n)
n+=1
return Found

You can use a couple tricks to speed things up, using the basic sieve of erastothenes. One is to use Wheel Factorization to skip calculating numbers that are known not to be prime. For example, besides 2 and 3, all primes are congruent to 1 or 5 mod 6. This means you don't have to process 4 of every 6 numbers at all.
At the next level, all primes are congruent to 1, 7, 11, 13, 17, 19, 23, or 29, mod 30. You can throw out 22 of every 30 numbers.
Here is a simple implementation of the sieve of Erastothenes that doesn't calculate or store even numbers:
def basic_gen_primes(n):
"""Return a list of all primes less then or equal to n"""
if n < 2:
return []
# The sieve. Each entry i represents (2i + 1)
size = (n + 1) // 2
sieve = [True] * size
# 2(0) + 1 == 1 is not prime
sieve[0] = False
for i, value in enumerate(sieve):
if not value:
continue
p = 2*i + 1
# p is prime. Remove all of its multiples from the sieve
# p^2 == (2i + 1)(2i + 1) == (4i^2 + 4i + 1) == 2(2i^2 + 2i) + 1
multiple = 2 * i * i + 2 * i
if multiple >= size:
break
while multiple < size:
sieve[multiple] = False
multiple += p
return [2] + [2*i+1 for i, value in enumerate(sieve) if value]
As mentioned, you can use more exotic sieves as well.

You can check only odd numbers. So why don't you use n+=2 instead of n+=1?

google and wikipedia for better algorithms. If you are only looking for small primes this might be fast enough. But the real algorithms are a lot faster for large primes.
http://en.wikipedia.org/wiki/Quadratic_sieve
start with that page.
Increment n by two instead of one. ?

Related

Python: take multiples based on condition

Is there a Way in Python to have a for loop which cycle only on multiples of a given number that are not multiples of any number lower than given number?
I mean something like this:
if given_number = 13
the for loop is going to cycle only on [13 * 13, 13 * 17, 13 * 19, 13 * 23, 13*29, 13*31, 13*39, ........]
I came across this kind of problem working on this function:
def get_primes_lower_n(n: int) -> List[int]:
"""
n: int
the given input number
returns: List[int]
all primes number lower than given input number
raises:
ValueError: the given input is an integer < 2
TypeError: the given input is not an integer
"""
if not isinstance(n, int):
raise TypeError("an integer is required")
if n < 2:
raise ValueError("an integer > 1 is required")
primes = np.ones(n + 1, dtype=bool) # bool(1) = True
primes[:2] = False
primes[4:: 2] = False
for i in range(3, isqrt(n) + 1, 2):
if primes[i]:
primes[i ** 2:: 2 * i] = False
return np.where(primes == True)[0]
if n is something like 9 * 10 ** 9 the algorithm is going to explode. For any value of I primes[i ** 2:: 2 * i] = False is going to take more or less 52 seconds. Let's think of I = 7; primes[i ** 2:: 2 * i] = False
is going to set to False all values in positions multiples of 21 and 35 that are already set to False.
As the value of I increases, I expect the time of execution of this operation primes[i ** 2:: 2 * i] = False to take less time (less values need to be set), but instead it increases exponentially. Why?
To answer your first question:
Is there a Way in python to have a for loop which cycle only on multiples of a given number that are not multiples of any number lower than given number?
Yes there is, but not efficient enough (unless you find a faster implementation).
Let n be the number we are looking at.
The numbers that are not multiples of any number lower than n are exactly the numbers left from the sieve when ran up to n. Therefore, these numbers are already present in the array, they are the True values with index greater than n. Unfortunately, as these are stored by index, finding them is expensive and finally makes the code go slower, as the collisions from the original algorithm are so rare. Still, here is a possible implementation in the for loop.
for i in range(3, isqrt(n) + 1, 2):
primes[i*(np.nonzero(primes[i:n//i])[0]+i)] = False
# [i:n//i] is to bound the search from i to the last number such
# that i*(n//i) < n.
Here is an equivalent code without the bool array:
def get_primes_lower_n(n: int) -> list[int]:
primes = np.arange(3, n + 1, 2) # only odd numbers over 3
val = 0
idx = -1
while val <= isqrt(n)+1:
idx += 1
val = primes[idx]
primes = np.setdiff1d(primes, val*primes[idx:idx+bisect.bisect_left(primes[idx:], n//val+1)])
return np.insert(primes, 0, 2)
As a conclusion, it is worth it to set the same values multiple times rather than use an exact approch that is slower.
Sorry for the lack of working solution but i hope this can help you in some way. If you find an interesting algorithm, feel free to let me know!

Euler Project 47 finding all numbers contaning 4 factors

I first create a list of 10^6 false values and what I want to do is to iterate True over the interval for all numbers containing 4 distinct prime factors.
What that means is that the number 2 * 2 * 3 * 5 * 7 is a number containing 4 distinct prime numbers.
I really have no clue how to create the numbers, I don't even know how to think of the it. I want to have 4 different kinds of numbers but in all possible different amounts. Code as far:
""" Pre do prime list """
sieve = [True] * 1000
sieve[0] = sieve[1] = False
def primes(sieve, x):
for i in range(x+x, len(sieve), x):
sieve[i] = False
for x in range(2, int(len(sieve) ** 0.5) + 1):
primes(sieve, x)
PRIMES = list((x for x in range(2, len(sieve)) if sieve[x]))
""" Main """
Numbers = [False] * 10 ** 6
Factors = PRIMES[0] * PRIMES[1] * PRIMES[2] * PRIMES[3]
Numbers[Factors] = True
for prime in PRIMES:
for prime in PRIMES[1:]:
for prime in PRIMES[2:]:
for prime in PRIMES[3:]:
I think the easiest way is to keep track of how many prime factors you have found for each number. You can perform the Sieve of Eratosthenes, but instead of marking multiples of a prime as composite, increment the count of primes dividing them. Make sure that you use an unoptimized loop: Once you choose the prime p, increment the count of primes dividing p, 2*p, 3*p etc. instead of marking p^2, p^2+2*p, etc. composite.
Another possibility is to record the smallest prime factor of each number as you perform the Sieve of Eratosthenes. This lets you find the prime factorization recursively, and you can check which of these have exactly 4 prime factors.
could you do it the other way round: get a list of primes up to root(N) then generate products that are less than N. Something like:
res = {}
for i in range(n):
for j in range(i,n):
for k in range(j,n):
for m in range(k,n):
prod = p[i] * p[j] * p[k] * p[m]
if prod < N:
res[prod] = [p[i], p[j], p[k], p[m]]
ed. just noticed distinct so you would have to put each p[] ** u and iterate each over a suitable number with another four nested loops! Probably still faster to do it this way round.
PPS after a little thought, the above method will be significantly slower than just using the modified sieve as suggested by Douglas Zare. By the time I get to 10**6 my first suggestion takes minutes but the modified sieve is less than 10s
class Numb(object):
def __init__(self):
self.is_prime = True
self.pf = []
def tick(self, factor):
self.is_prime = False
self.pf.append(factor)
N = 1000000
sieve = [Numb() for i in range(N)]
sieve[0].is_prime = sieve[1].is_prime = False
def primes(sieve, x):
for i in range(x + x, len(sieve), x):
sieve[i].tick(x)
for x in range(2, int(len(sieve) ** 0.5) + 1):
if sieve[x].is_prime:
primes(sieve, x)
I continued doing something like this, before trying out sieve method.. And in the end realising that I should use an search algorithm that finds two odd number with 4 distinct factors which differ by 2 and try the number in-between and the number before and after. if this condition is meet the problem is solved.
It's actually falls back on the same problem as stated in post but through some number-theory magic reduces to just finding numbers with no multiples of factors where question conditions is meet.
Factors = list([0, 0, 1, 1, 2, 1, 2, 1, 3, 2]) + [0] * 5 * 10 ** 5
for prime in PRIMES:
Factors[prime] = 1
for number in range(10, 5 * 10 ** 5):
if Factors[number] == 1:
continue
for prime in PRIMES:
if number % prime == 0:
Factors[number] = Factors[prime] + Factors[number // prime]
break

Sieve of Eratosthenes - Primes between X and N

I found this highly optimised implementation of the Sieve of Eratosthenes for Python on Stack Overflow. I have a rough idea of what it's doing but I must admit the details of it's workings elude me.
I would still like to use it for a little project (I'm aware there are libraries to do this but I would like to use this function).
Here's the original:
'''
Sieve of Eratosthenes
Implementation by Robert William Hanks
https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n/3035188
'''
def sieve(n):
"""Return an array of the primes below n."""
prime = numpy.ones(n//3 + (n%6==2), dtype=numpy.bool)
for i in range(3, int(n**.5) + 1, 3):
if prime[i // 3]:
p = (i + 1) | 1
prime[ p*p//3 ::2*p] = False
prime[p*(p-2*(i&1)+4)//3::2*p] = False
result = (3 * prime.nonzero()[0] + 1) | 1
result[0] = 3
return numpy.r_[2,result]
What I'm trying to achieve is to modify it to return all primes below n starting at x so that:
primes = sieve(50, 100)
would return primes between 50 and 100. This seemed easy enough, I tried replacing these two lines:
def sieve(x, n):
...
for i in range(x, int(n**.5) + 1, 3):
...
But for a reason I can't explain, the value of x in the above has no influence on the numpy array returned!
How can I modify sieve() to only return primes between x and n
The implementation you've borrowed is able to start at 3 because it replaces sieving out the multiples of 2 by just skipping all even numbers; that's what the 2*… that appear multiple times in the code are about. The fact that 3 is the next prime is also hardcoded in all over the place, but let's ignore that for the moment, because if you can't get past the special-casing of 2, the special-casing of 3 doesn't matter.
Skipping even numbers is a special case of a "wheel". You can skip sieving multiples of 2 by always incrementing by 2; you can skip sieving multiples of 2 and 3 by alternately incrementing by 2 and 4; you can skip sieving multiples of 2, 3, 5, and 7 by alternately incrementing by 2, 4, 2, 4, 6, 2, 6, … (there's 48 numbers in the sequence), and so on. So, you could extend this code by first finding all the primes up to x, then building a wheel, then using that wheel to find all the primes between x and n.
But that's adding a lot of complexity. And once you get too far beyond 7, the cost (both in time, and in space for storing the wheel) swamps the savings. And if your whole goal is not to find the primes before x, finding the primes before x so you don't have to find them seems kind of silly. :)
The simpler thing to do is just find all the primes up to n, and throw out the ones below x. Which you can do with a trivial change at the end:
primes = numpy.r_[2,result]
return primes[primes>=x]
Or course there are ways to do this without wasting storage for those initial primes you're going to throw away. They'd be a bit complicated to work into this algorithm (you'd probably want to build the array in sections, then drop each section that's entirely < x as you go, then stack all the remaining sections); it would be far easier to use a different implementation of the algorithm that isn't designed for speed and simplicity over space…
And of course there are different prime-finding algorithms that don't require enumerating all the primes up to x in the first place. But if you want to use this implementation of this algorithm, that doesn't matter.
Since you're now interested in looking into other algorithms or other implementations, try this one. It doesn't use numpy, but it is rather fast. I've tried a few variations on this theme, including using sets, and pre-computing a table of low primes, but they were all slower than this one.
#! /usr/bin/env python
''' Prime range sieve.
Written by PM 2Ring 2014.10.15
For range(0, 30000000) this is actually _faster_ than the
plain Eratosthenes sieve in sieve3.py !!!
'''
import sys
def potential_primes():
''' Make a generator for 2, 3, 5, & thence all numbers coprime to 30 '''
s = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
for i in s:
yield i
s = (1,) + s[3:]
j = 30
while True:
for i in s:
yield j + i
j += 30
def range_sieve(lo, hi):
''' Create a list of all primes in the range(lo, hi) '''
#Mark all numbers as prime
primes = [True] * (hi - lo)
#Eliminate 0 and 1, if necessary
for i in range(lo, min(2, hi)):
primes[i - lo] = False
ihi = int(hi ** 0.5)
for i in potential_primes():
if i > ihi:
break
#Find first multiple of i: i >= i*i and i >= lo
ilo = max(i, 1 + (lo - 1) // i ) * i
#Determine how many multiples of i >= ilo are in range
n = 1 + (hi - ilo - 1) // i
#Mark them as composite
primes[ilo - lo : : i] = n * [False]
return [i for i,v in enumerate(primes, lo) if v]
def main():
lo = int(sys.argv[1]) if len(sys.argv) > 1 else 0
hi = int(sys.argv[2]) if len(sys.argv) > 2 else lo + 30
#print lo, hi
primes = range_sieve(lo, hi)
#print len(primes)
print primes
#print primes[:10], primes[-10:]
if __name__ == '__main__':
main()
And here's a link to the plain Eratosthenes sieve that I mentioned in the docstring, in case you want to compare this program to that one.
You could improve this slightly by getting rid of the loop under #Eliminate 0 and 1, if necessary. And I guess it might be slightly faster if you avoided looking at even numbers; it'd certainly use less memory. But then you'd have to handle the cases when 2 was inside the range, and I figure that the less tests you have the faster this thing will run.
Here's a minor improvement to that code: replace
#Mark all numbers as prime
primes = [True] * (hi - lo)
#Eliminate 0 and 1, if necessary
for i in range(lo, min(2, hi)):
primes[i - lo] = False
with
#Eliminate 0 and 1, if necessary
lo = max(2, lo)
#Mark all numbers as prime
primes = [True] * (hi - lo)
However, the original form may be preferable if you want to return the plain bool list rather than performing the enumerate to build a list of integers: the bool list is more useful for testing if a given number is prime; OTOH, the enumerate could be used to build a set rather than a list.

python prime factorization performance

I'm relatively new to python and I'm confused about the performance of two relatively simple blocks of code. The first function generates a prime factorization of a number n given a list of primes. The second generates a list of all factors of n. I would have though prime_factor would be faster than factors (for the same n), but this is not the case. I'm not looking for better algorithms, but rather I would like to understand why prime_factor is so much slower than factors.
def prime_factor(n, primes):
prime_factors = []
i = 0
while n != 1:
if n % primes[i] == 0:
factor = primes[i]
prime_factors.append(factor)
n = n // factor
else: i += 1
return prime_factors
import math
def factors(n):
if n == 0: return []
factors = {1, n}
for i in range(2, math.floor(n ** (1/2)) + 1):
if n % i == 0:
factors.add(i)
factors.add(n // i)
return list(factors)
Using the timeit module,
{ i:factors(i) for i in range(1, 10000) } takes 2.5 seconds
{ i:prime_factor(i, primes) for i in range(1, 10000) } takes 17 seconds
This is surprising to me. factors checks every number from 1 to sqrt(n), while prime_factor only checks primes. I would appreciate any help in understanding the performance characteristics of these two functions.
Thanks
Edit: (response to roliu)
Here is my code to generate a list of primes from 2 to up_to:
def primes_up_to(up_to):
marked = [0] * up_to
value = 3
s = 2
primes = [2]
while value < up_to:
if marked[value] == 0:
primes.append(value)
i = value
while i < up_to:
marked[i] = 1
i += value
value += 2
return primes
Without seeing what you used for primes, we have to guess (we can't run your code).
But a big part of this is simply mathematics: there are (very roughly speaking) about n/log(n) primes less than n, and that's a lot bigger than sqrt(n). So when you pass a prime, prime_factor(n) does a lot more work: it goes through O(n/log(n)) operations before finding the first prime factor (n itself!), while factors(n) gives up after O(sqrt(n)) operations.
This can be very significant. For example, sqrt(10000) is just 100, but there are 1229 primes less than 10000. So prime_factor(n) can need to do over 10 times as much work to deal with the large primes in your range.

Optimise the solution to Project Euler 12 (Python)

I have the following code for Project Euler Problem 12. However, it takes a very long time to execute. Does anyone have any suggestions for speeding it up?
n = input("Enter number: ")
def genfact(n):
t = []
for i in xrange(1, n+1):
if n%i == 0:
t.append(i)
return t
print "Numbers of divisors: ", len(genfact(n))
print
m = input("Enter the number of triangle numbers to check: ")
print
for i in xrange (2, m+2):
a = sum(xrange(i))
b = len(genfact(a))
if b > 500:
print a
For n, I enter an arbitrary number such as 6 just to check whether it indeed returns the length of the list of the number of factors.
For m, I enter entered 80 000 000
It works relatively quickly for small numbers. If I enter b > 50 ; it returns 28 for a, which is correct.
My answer here isn't pretty or elegant, it is still brute force. But, it simplifies the problem space a little and terminates successfully in less than 10 seconds.
Getting factors of n:
Like #usethedeathstar mentioned, it is possible to test for factors only up to n/2. However, we can do better by testing only up to the square root of n:
let n = 36
=> factors(n) : (1x36, 2x18, 3x12, 4x9, 6x6, 9x4, 12x3, 18x2, 36x1)
As you can see, it loops around after 6 (the square root of 36). We also don't need to explicitly return the factors, just find out how many there are... so just count them off with a generator inside of sum():
import math
def get_factors(n):
return sum(2 for i in range(1, round(math.sqrt(n)+1)) if not n % i)
Testing the triangular numbers
I have used a generator function to yield the triangular numbers:
def generate_triangles(limit):
l = 1
while l <= limit:
yield sum(range(l + 1))
l += 1
And finally, start testing:
def test_triangles():
triangles = generate_triangles(100000)
for i in triangles:
if get_factors(i) > 499:
return i
Running this with the profiler, it completes in less than 10 seconds:
$ python3 -m cProfile euler12.py
361986 function calls in 8.006 seconds
The BIGGEST time saving here is get_factors(n) testing only up to the square root of n - this makes it heeeaps quicker and you save heaps of memory overhead by not generating a list of factors.
As I said, it still isn't pretty - I am sure there are more elegant solutions. But, it fits the bill of being faster :)
I got my answer to run in 1.8 seconds with Python.
import time
from math import sqrt
def count_divisors(n):
d = {}
count = 1
while n % 2 == 0:
n = n / 2
try:
d[2] += 1
except KeyError:
d[2] = 1
for i in range(3, int(sqrt(n+1)), 2):
while n % i == 0 and i != n:
n = n / i
try:
d[i] += 1
except KeyError:
d[i] = 1
d[n] = 1
for _,v in d.items():
count = count * (v + 1)
return count
def tri_number(num):
next = 1 + int(sqrt(1+(8 * num)))
return num + (next/2)
def main():
i = 1
while count_divisors(i) < 500:
i = tri_number(i)
return i
start = time.time()
answer = main()
elapsed = (time.time() - start)
print("result %s returned in %s seconds." % (answer, elapsed))
Here is the output showing the timedelta and correct answer:
$ python ./project012.py
result 76576500 returned in 1.82238006592 seconds.
Factoring
For counting the divisors, I start by initializing an empty dictionary and a counter. For each factor found, I create key of d[factor] with value of 1 if it does not exist, otherwise, I increment the value d[factor].
For example, if we counted the factors 100, we would see d = {25: 1, 2: 2}
The first while loop, I factor out all 2's, dividing n by 2 each time. Next, I begin factoring at 3, skipping two each time (since we factored all even numbers already), and stopping once I get to the square root of n+1.
We stop at the square_root of n because if there's a pair of factors with one of the numbers bigger than square_root of n, the other of the pair has to be less than 10. If the smaller one doesn't exist, there is no matching larger factor.
https://math.stackexchange.com/questions/1343171/why-only-square-root-approach-to-check-number-is-prime
while n % 2 == 0:
n = n / 2
try:
d[2] += 1
except KeyError:
d[2] = 1
for i in range(3, int(sqrt(n+1)), 2):
while n % i == 0 and i != n:
n = n / i
try:
d[i] += 1
except KeyError:
d[i] = 1
d[n] = 1
Now that I have gotten each factor, and added it to the dictionary, we have to add the last factor (which is just n).
Counting Divisors
Now that the dictionary is complete, we loop through each of the items, and apply the following formula: d(n)=(a+1)(b+1)(c+1)...
https://www.wikihow.com/Determine-the-Number-of-Divisors-of-an-Integer
All this formula means is taking all of the counts of each factor, adding 1, then multiplying them together. Take 100 for example, which has factors 25, 2, and 2. We would calculate d(n)=(a+1)(b+1) = (1+1)(2+1) = (2)(3) = 6 total divisors
for _,v in d.items():
count = count * (v + 1)
return count
Calculate Triangle Numbers
Now, taking a look at tri_number(), you can see that I opted to calculate the next triangle number in a sequence without manually adding each whole number together (saving me millions of operations). Instead I used T(n) = n (n+1) / 2
http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/runsums/triNbProof.html
We are providing a whole number to the function as an argument, so we need to solve for n, which is going to be the whole number to add next. Once we have the next number (n), we simply add that single number to num and return
S=n(n+1)2
S=n2+n2
2S=n2+n
n2+n−2S=0
At this point, we use the quadratic formula for : ax2+bx+c=0.
n=−b±√b2−4ac / 2a
n=−1±√1−4(1)(−2S) / 2
n=−1±√1+8S / 2
https://socratic.org/questions/how-do-you-solve-for-n-in-s-n-n-1-2
So all tri_number() does is evaluate n=1+√1+8S / 2 (we ignore the negative equation here). The answer that is returned is the next triangle number in the sequence.
def tri_number(num):
next = 1 + int(sqrt(1+(8 * num)))
return num + (next/2)
Main Loop
Finally, we can look at main(). We start at whole number 1. We count the divisor of 1. If it is less than 500, we get the next triangle number, then try again and again until we get a number with > 500 divisors.
def main():
i = 1
while count_divisors(i) < 500:
i = tri_number(i)
return i
I am sure there are additional ways to optimize but I am not smart enough to understand those ways. If you find any better ways to optimize python, let me know! I originally solved project 12 in Golang, and that run in 25 milliseconds!
$ go run project012.go
76576500
2018/07/12 01:56:31 TIME: main() took 23.581558ms
one of the hints i can give is
def genfact(n):
t = []
for i in xrange(1, n+1):
if n%i == 0:
t.append(i)
return t
change that to
def genfact(n):
t=[]
for i in xrange(1,numpy.sqrt(n)+1):
if(n%i==0):
t.append(i)
t.apend(n/i)
since if a is a divisor than so is b=n/a, since a*b=a*n/b=n, That should help a part already (not sure if in your case a square is possible, but if so, add another case to exclude adding the same number twice)
You could devise a recursive thing too, (like if it is something like for 28, you get 1,28,2,14 and at the moment you are at knowing 14, you put in something to actually remember the divisors of 14 (memoize), than check if they are alraedy in the list, and if not, add them to the list, together with 28/d for each of the divisors of 14, and at the end just take out the duplicates
If you think my first answer is still not fast enough, ask for more, and i will check how it would be done to solve it faster with some more tricks (could probably make use of erastothenes sieve or so too, and some other tricks could be thought up as well if you would wish to really blow up the problem to huge proportions, like to check the first one with over 10k divisors or so)
while True:
c=0
n=1
m=1
for i in range(1,n+1):
if n%i==0:
c=c+1
m=m+1
n=m*(m+1)/2
if c>500:
break
print n
this is not my code but it is so optimized.
source: http://code.jasonbhill.com/sage/project-euler-problem-12/
import time
def num_divisors(n):
if n % 2 == 0: n = n / 2
divisors = 1
count = 0
while n % 2 == 0:
count += 1
n = n / 2
divisors = divisors * (count + 1)
p = 3
while n != 1:
count = 0
while n % p == 0:
count += 1
n = n / p
divisors = divisors * (count + 1)
p += 2
return divisors
def find_triangular_index(factor_limit):
n = 1
lnum, rnum = num_divisors(n), num_divisors(n + 1)
while lnum * rnum < 500:
n += 1
lnum, rnum = rnum, num_divisors(n + 1)
return n
start = time.time()
index = find_triangular_index(500)
triangle = (index * (index + 1)) / 2
elapsed = (time.time() - start)
print("result %s returned in %s seconds." % (triangle, elapsed))

Categories