Related
I have several days struggling with this Prime Generator algorithm for SPOJ problem. The problem state to print at least 100000 primes from a number m,n with n<=1000000000 in 6 seconds. I have this implementation that print 100000 prime in 11.701067686080933 seconds. is it possible to beat the time restriction(6s) in Python.
I feel that I miss something in my segmented sieve function , cause I just implemented it how I understand the algorithm work, maybe a change can make it better.
Some Help would appreciated here.
def sieveOfErosthen(m):
limit=m+1
prime=[True]*limit
for i in range(2,int(m**0.5)):
if prime[i]:
for x in range(i*i,limit,i):
prime[x]=False
return prime
def segmentedSieve(m,n):
limit= n+1
segment=[True]*limit
for j in range(2,int(n**0.5) ):
if sieveOfErosthen(j):
for b in range(j*(m//j),limit,j):
if b >j:
segment[b]=False
for v in range(m,limit):
if segment[v]:
print(v)
return True
This code is a disaster. Let's begin with the most glaring error:
if sieveOfErosthen(j):
(This is particularly confusing as your original code didn't define this function but instead defined EratosthenesSieve() -- later editors of your post mapped one onto the other which I'm assuming is correct.) What does sieveOfErosthen(j) return? It returns an array, so in the boolean context of if, this test is always True, as the array always contains at least one element if j is positive!
Change this to if True: and see that your output doesn't change. What's left is a very inefficient sieve algorithm, which we can speed up in various ways:
def segmentedSieve(m, n):
primes = []
limit = n + 1
segment = [True] * limit
if limit > 0:
segment[0] = False
if limit > 1:
segment[1] = False
for j in range(2, int(limit**0.5) + 1):
if segment[j]:
for b in range(j * j, limit, j):
segment[b] = False
for v in range(m, limit):
if segment[v]:
primes.append(v)
return primes
This code can easily find the first 100,000 primes in a fraction of a second, But ultimately, if n <= 1000000000 (a billion) then we have to assume the worst case, i.e. the last 100,000 primes in 6 seconds for some suitable m in segmentedSieve(m, 1000000000) which will take this code minutes not seconds.
Finally, you didn't implement a segmented sieve -- you implemented a regular sieve and just skimmed off the requested range. I recommend you read about segmented sieves in Wikipedia, or elsewhere, and start over if you need a segmented sieve.
For solving this problem you have to use Segmented sieve.
there are some good resources please check these
geeksforgeeks
quora
https://discuss.codechef.com/questions/54416/segmented-sieve
https://github.com/calmhandtitan/algorepo/blob/master/numberTheory/sieve_fast.cpp
This is a question regarding a code challenge, please don't supply too much code.. I'd like to figure this out myself as much as possible.
I recently started getting into code challenges, and combined it with learning Python (I'm a frontend javascript developer by day ;) ). All is going well so far and I'm convinced that this is the best way to learn a new language (for me at least).
I'm currently stuck on a challenge that requires me to print all prime numbers in a given range, this is all done by simple Stdin and Stdout.
I've tried two approaches so far, both are working but are too slow.. below is a link and the code of my fastest implementation so far. Maybe I'm missing something super obvious that is causing my python script to slow down. Currently it takes 1.76s for a single test case with a range of 1, 100000
http://ideone.com/GT6Xxk (you can debug the script here as well)
from sys import stdin
from math import sqrt, ceil
next(stdin) # skip unecessary line that describes the number of test cases
def is_prime(number):
initial_divider = sqrt(number)
if number == 2:
return True
elif number % 2 == 0 or int(initial_divider) == initial_divider:
return False
for divider in range(ceil(initial_divider), 1, -1):
if number % divider == 0:
return False
return True
for line in stdin:
low, high = [int(x) for x in line.split(' ')]
primes = [number for number
in range(max(low, 2), high+1)
if is_prime(number)]
for prime in primes:
print (prime)
print('')
The description of the 'assignment' / challenge is as follows:
Input
The input begins with the number t of test cases in a single line (t<=10). In >each of the next t lines there are two numbers m and n (1 <= m <= n <= >1000000000, n-m<=100000) separated by a space.
Output
For every test case print all prime numbers p such that m <= p <= n, one number >per line, test cases separated by an empty line.
Update 1: I cleaned up the logic of the last block, where the gathering of primes and printing is done:
for line in stdin:
low, high = [int(x) for x in line.split(' ')]
for number in range(max(low, 2), high+1):
if is_prime(number):
print (number)
print('')
1) It might be dominated by console IO, printing the output. I changed the output so it uses a generator to collect the primes, convert the numbers to strings, and join the numbers with newlines. This should save some memory in list building and push some Python list iteration down into the Python runtime. That made it ~30% faster in unscientific rushed testing on my PC, doesn't make much difference on ideone. (This might be because I bodged it to run in Python 2, which has some very different iteration/list/generator workings, but used Python 3 on ideone).
2) You run the if number == 2: return True test every time; out of the first 100,000 numbers, most of them aren't 2. I extracted that to print 2 before printing the rest of the primes. Very minor change, not really worth it.
3) Your range counts down - range(ceil(initial_divider), 1, -1) - and that's really weird. It's very much more likely that a number will divide by 3 than by, say, 19. Every third number divides by 3, only every 19th number divides by 19. So for quick-return of the function, try the small dividers first, right? I set it to count up. Noticable speed improvement, I hope it's still working.
That's ~50% of the original runtime, in a casual and completely uncomparable situation. Code now looks like this:
from sys import stdin
from math import sqrt, ceil
next(stdin) # skip unecessary line
def is_prime(number):
initial_divider = sqrt(number)
if number % 2 == 0 or int(initial_divider) == initial_divider:
return False
for divider in range(2, ceil(initial_divider)+1):
if number % divider == 0:
return False
return True
for line in stdin:
low, high = [int(x) for x in line.split(' ')]
primes = '\n'.join(str(number) for number
in range(max(low, 3), high+1)
if is_prime(number))
if low <= 2: print(2)
print (primes)
print('')
Change list comprehension to generator, the script will run faster.
for number in range(max(low, 2), high+1):
if is_prime(number):
yield number
In a language like C or C++ the SPOJ PRIME 1 problem can easily be solved by brute force, i.e. by writing code that sieves all numbers up to 1000,000,000 in less than a second and thus stays below the time limit. Perhaps even in Java, C# or Delphi. But if it is possible in Python at all then it is probably bloody hard and requires some serious fu.
Note, however, that SPOJ PRIME 1 does not ask for a billion numbers to be sieved; it asks for a couple of small ranges to be sieved which are no wider than 100001 numbers, and it likely queries only a handful of ranges. Let's say the number of ranges is 100 (it's probably much less) and the average width is 100000 (it's probably much less) then that's still only 10,000,000 numbers. Sieving the full billion in that situation does two orders of magnitude too much work, which is why SPOJ PRIME 1 manages to weed out the chaff with such precision despite the wide range of languages employed by pundits.
Hence the trick is to do only what's asked - to sieve the ranges provided as input. Even the most simple, straightforward code can do that with lots of time to spare (C++: about a millisecond total). The principle is exactly the same as in my answer to the challenge of drawing 100 random primes from a range with an upper bound of 1,000,000,000, and the same solution applies. The key is writing a function that can sieve a given range (window) without having to sieve all numbers below as well.
Besides, the question of how to beat SPOJ PRIME 1 has been asked numerous times already and the answers given are still valid. A small selection:
How do I efficiently sieve through a selected range for prime numbers?
Efficient algorithm to get primes between two large numbers
Generating prime numbers between m and n
SPOJ PRIME1 : TLE
Segmented Sieve of Erastothenes C++ SPOJ
Spoj PRIME1 using sieve of eratosthenes (in C)
...
def PrimesBelow(limit):
np=set()
p=[2]
for i in xrange(1,limit+1,2):
if i == 1: continue
if i in np: continue
beg=2 if i % 2 == 0 else 0
for j in xrange(beg,int(limit)+1,i):
np.add(j)
p.append(i)
return i
LetzerWille was right. Function above will return a list of prime numbers below (limit). I think that this function will run faster than checking each number is prime or not, because this function will remove multiplies of each number and add them to (np).
Side note: this function will test odd numbers only.
Simple improvement. It was embarrasing to see this simple code. Mine was much longer and slower :( ... but I learned a lot :)
Adding also simple function for measuring time mt()
def PrimesBelow(limit):
np=set()
p=[2]
for i in range(3,limit+1,2):
if i in np: continue
beg = i*i
for j in range(beg,int(limit)+1,i):
np.add(j)
p.append(i)
return p
def mt(n):
import time
t = time.time()
pr = PrimesBelow(n)
print("#-Primes: {}, Time: {}".format(len(pr), round(time.time()-t, 4)))
return pr
pr = mt(100000000)
is about 49 secs on a i7-3770 with 16GB
This will be the optimized code with less number of executions, it can calculate and display 10000 prime numbers within a second.
it uses the prime number property that
* if a number is not divisible by the numbers which are less than its square root then it is prime number.
* instead of checking till the end(Means 1000 iteration to figure out 1000 is prime or not) we can end the loop within 35 iterations,
* break the loop if it is divided by any number at the beginning(if it is even loop will break on first iteration, if it is divisible by 3 then 2 iteration) so we iterate till the end only for the prime numbers
remember one thing you can still optimize the iterations by using the property *if a number is not divisible with the prime numbers less than that then it is prime number but the code will be too large, we have to keep track of the calculated prime numbers, also it is difficult to find a particular number is a prime or not, so this will be the Best logic or code
import math
number=1
count = 0
while(count<10000):
isprime=1
number+=1
for j in range(2,int(math.sqrt(number))+1):
if(number%j==0):
isprime=0
break
if(isprime==1):
print(number,end=" ")
count+=1
print("\nCount "+str(count))
The problem is:
Given a range of numbers (x,y) , Find all the prime numbers(Count only) which are sum of the squares of two numbers, with the restriction that 0<=x<y<=2*(10^8)
According to Fermat's theorem :
Fermat's theorem on sums of two squares asserts that an odd prime number p can be
expressed as p = x^2 + y^2 with integer x and y if and only if p is congruent to
1 (mod4).
I have done something like this:
import math
def is_prime(n):
if n % 2 == 0 and n > 2:
return False
return all(n % i for i in range(3, int(math.sqrt(n)) + 1, 2))
a,b=map(int,raw_input().split())
count=0
for i in range(a,b+1):
if(is_prime(i) and (i-1)%4==0):
count+=1
print(count)
But this increases the time complexity and memory limit in some cases.
Here is my submission result:
Can anyone help me reduce the Time Complexity and Memory limit with better algorithm?
Problem Link(Not an ongoing contest FYI)
Do not check whether each number is prime. Precompute all the prime numbers in the range, using Sieve of Eratosthenes. This will greatly reduce the complexity.
Since you have maximum of 200M numbers and 256Mb memory limit and need at least 4 bytes per number, you need a little hack. Do not initialize the sieve with all numbers up to y, but only with numbers that are not divisible by 2, 3 and 5. That will reduce the initial size of the sieve enough to fit into the memory limit.
UPD As correctly pointed out by Will Ness in comments, sieve contains only flags, not numbers, thus it requires not more than 1 byte per element and you don't even need this precomputing hack.
You can reduce your memory usage by changing for i in range(a,b+1): to for i in xrange(a,b+1):, so that you are not generating an entire list in memory.
You can do the same thing inside the statement below, but you are right that it does not help with time.
return all(n % i for i in xrange(3, int(math.sqrt(n)) + 1, 2))
One time optimization that might not cost as much in terms of memory as the other answer is to use Fermat's Little Theorem. It may help you reject many candidates early.
More specifically, you could pick maybe 3 or 4 random values to test and if one of them rejects, then you can reject. Otherwise you can do the test you are currently doing.
First of all, although it will not change the order of your time-complexity, you can still narrow down the list of numbers that you are checking by a factor of 6, since you only need to check numbers that are either equal to 1 mod 12 or equal to 5 mod 12 (such as [1,5], [13,17], [25,29], [37,41], etc).
Since you only need to count the primes which are sum of squares of two numbers, the order doesn't matter. Therefore, you can change range(a,b+1) to range(1,b+1,12)+range(5,b+1,12).
Obviously, you can then remove the if n % 2 == 0 and n > 2 condition in function is_prime, and in addition, change the if is_prime(i) and (i-1)%4 == 0 condition to if is_prime(i).
And finally, you can check the primality of each number by dividing it only with numbers that are adjacent to multiples of 6 (such as [5,7], [11,13], [17,19], [23,25], etc).
So you can change this:
range(3,int(math.sqrt(n))+1,2)
To this:
range(5,math.sqrt(n))+1,6)+range(7,math.sqrt(n))+1,6)
And you might as well calculate math.sqrt(n))+1 beforehand.
To summarize all this, here is how you can improve the overall performance of your program:
import math
def is_prime(n):
max = int(math.sqrt(n))+1
return all(n % i for i in range(5,max,6)+range(7,max,6))
count = 0
b = int(raw_input())
for i in range(1,b+1,12)+range(5,b+1,12):
if is_prime(i):
count += 1
print count
Please note that 1 is typically not regarded as prime, so you might want to print count-1 instead. On the other hand, 2 is not equal to 1 mod 4, yet it is the sum of two squares, so you may leave it as is...
The 10th problem in Project Euler:
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
Find the sum of all the primes below two million.
I found this snippet :
sieve = [True] * 2000000 # Sieve is faster for 2M primes
def mark(sieve, x):
for i in xrange(x+x, len(sieve), x):
sieve[i] = False
for x in xrange(2, int(len(sieve) ** 0.5) + 1):
if sieve[x]: mark(sieve, x)
print sum(i for i in xrange(2, len(sieve)) if sieve[i])
published here
which run for 3 seconds.
I wrote this code:
def isprime(n):
for x in xrange(3, int(n**0.5)+1):
if n % x == 0:
return False
return True
sum=0;
for i in xrange(1,int(2e6),2):
if isprime(i):
sum += i
I don't understand why my code (the second one) is much slower?
Your algorithm is checking every number individually from 2 to N (where N=2000000) for primality.
Snippet-1 uses the sieve of Eratosthenes algorithm, discovered about 2200 years ago.
It does not check every number but:
Makes a "sieve" of all numbers from 2 to 2000000.
Finds the first number (2), marks it as prime, then deletes all its multiples from the sieve.
Then finds the next undeleted number (3), marks it as prime and deletes all its multiples from the sieve.
Then finds the next undeleted number (5), marks it as prime and deletes all its multiples from the sieve.
...
Until it finds the prime 1409 and deletes all its multiples from the sieve.
Then all primes up to 1414 ~= sqrt(2000000) have been found and it stops
The numbers from 1415 up to 2000000 do not have to be checked. All of them who have not been deleted are primes, too.
So the algorithm produces all primes up to N.
Notice that it does not do any division, only additions (not even multiplications, and not that it matters with so small numbers but it might with bigger ones). Time complexity is O(n loglogn) while your algorithm has something near O(n^(3/2)) (or O(n^(3/2) / logn) as #Daniel Fischer commented), assuming divisions cost the same as multiplications.
From the Wikipedia (linked above) article:
Time complexity in the random access machine model is O(n log log n) operations, a direct consequence of the fact that the prime harmonic series asymptotically approaches log log n.
(with n = 2e6 in this case)
The first version pre-computes all the primes in the range and stores them in the sieve array, then finding the solution is a simple matter of adding the primes in the array. It can be seen as a form of memoization.
The second version tests for each number in the range to see if it is prime, repeating a lot of work already made by previous calculations.
In conclusion, the first version avoids re-computing values, whereas the second version performs the same operations again and again.
To easily understand the difference, try thinking how many times each number will be used as a potential divider:
In your solution, the number 2 will be tested for EACH number when that number will be tested for being a prime. Every number you pass along the way will then be used as a potential divider for every next number.
In the first solution, once you stepped over a number you never look back - you always move forward from the place you reached. By the way, a possible and common optimization is to go for odd numbers only after you marked 2:
mark(sieve, 2)
for x in xrange(3, int(len(sieve) ** 0.5) + 1, 2):
if sieve[x]: mark(sieve, x)
This way you only look at each number once and clear out all of its multiplications forward, rather than going through all possible dividers again and again checking each number with all its predecessors, and the if statement prevents you from doing repeated work for a number you previously encountered.
As Óscar's answer indicates, your algorithm repeats a lot of work. To see just how much processing the other algorithm saves, consider the following modified version of the mark() and isprime() functions, which keep track of how many times the function has been called and the total number of for loop iterations:
calls, count = 0, 0
def mark(sieve, x):
global calls, count
calls += 1
for i in xrange(x+x, len(sieve), x):
count += 1
sieve[i] = False
After running the first code with this new function we can see that mark() is called 223 times with a total of 4,489,006 (~4.5 million) iterations in the for loop.
calls, count = 0
def isprime(n):
global calls, count
calls += 1
for x in xrange(3, int(n**0.5)+1):
count += 1
if n % x == 0:
return False
return True
If we make a similar change to your code, we can see that isprime() is called 1,000,000 (1 million) times with 177,492,735 (~177.5 million) iterations of the for loop.
Counting function calls and loop iterations isn't always a conclusive way to determine why an algorithm is faster, but generally less steps == less time, and clearly your code could use some optimization to reduce the number of steps.
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 11 years ago.
Improve this question
I'm a 17 year old getting started with programming with the help of the Python programming language.
I've been seeking to optimize this algorithm, perhaps by eliminating one of the loops, or with a better test to check for prime numbers.
Trying to calculate and display 100000 prime numbers has the script pausing for about 6 seconds as it populates the list with primes before the primes list is returned to the console as output.
I've been experimenting with using
print odd,
to simply print every found prime number, which is faster for smaller inputs like n = 1000, but for n = 1000000 the list itself prints much faster (both in the python shell and in the console).
Perhaps the entire code/algorithm should be revamped, but the script should remain essentially the same: The user types in the number of prime numbers to be printed (n) and the script returns all prime numbers up to the nth prime number.
from time import time
odd = 1
primes = [2]
n = input("Number of prime numbers to print: ")
clock = time()
def isPrime(number):
global primes
for i in primes:
if i*i > number:
return True
if number%i is 0:
return False
while len(primes) < n:
odd += 2
if isPrime(odd):
primes += [odd]
print primes
clock -= time()
print "\n", -clock
raw_input()
I might wanna rewrite the whole script to use a sieve like the Sieve of Atkin: http://en.wikipedia.org/wiki/Sieve_of_Atkin
However, I am simply a beginner at Python (or even at programming: I started writing code only 2 weeks ago) and it would be quite a challenge for me to figure out how to code a Sieve of Atkin algorithm in Python.
I wish a google hacker out there would hand hold me through stuff like this :(
You could use prime sieve, and with a simple twist:
Define the first prime 2 as you do, set the largest number reached (max) to 2;
Generate a list of n consecutive numbers from max+1 to max+n;
Use sieve with the primes on this list. When sieving, set the beginning number for each prime to the smallest number in the list that could be divided by the prime;
If the amount is not reacher, goto 2.
This way, you could control the length of the list, and as the length grows larger, the speed will be faster. However, this is a total rework of the algorithm, and is harder to program.
Here's a sample code, which is quite crude, but this only takes less than 70% time of the original:
from math import sqrt
from time import time
primes = [2]
max = 3
n = input("Number of prime numbers to print: ")
r=2
clock = time()
def sieve(r):
global primes
global max
s = set(range(max,max+r))
for i in primes:
b=max//i
if (b*i<max):
b=b+1
b=b*i
while b<=max+r-1:
if b in s:
s.remove(b)
b=b+i
for i in s:
primes.append(i)
while len(primes) < n:
r=primes[-1]
sieve(r)
max=max+r
primes=primes[0:n]
print primes
clock -= time()
print "\n", -clock
raw_input()
There are many ways to improve this, this just shows the notion of the approach.
Also, this can blow up the memory when the number is large. I used the dynamic limit try to somewhat relieve this.
And if you are really curious (and fearless), you could look at the more complicated implementations in various open source projects. One example is Pari/GP, which is written in C++, and is blazing fast (I tested 1 to 50000000 in less than 1 min, if I remember correctly). Translating them to Python may be hard, but will be helpful, perhaps not just for yourself;-)
One simple optimizations which could be applied without hacking the code completely.
the i*i on every prime gets very wasteful as the list gets longer. Instead calculate the square root of i outside the loop and test against this value inside the loop.
However square root is itself and expensive calculation and the majority of candidate numbers will be rejected as divisible by one of the lower primes (3,5,7) so this turns out to be not such a good optimization (pessimization?). But we don't actually need to be that precise and a simple check that the prime is less than one third of the value has a similar effect without the computational cost of the square root calculation, but, at the expense of a relatively few unnecessary test.
As was already said by Ziyao Wei I'd also try a Sieve implementation. The only thing I'd improve is to use the Prime number theorem as a starting point for the used size.
Computing the inverse function isn't straightforward in pure python, but an iterative approach should be good enough and that way you could get a pretty good idea how large the sieve would have to be. Since I don't really remember the proofs for the theorem in detail and it's 6am in the morning here, someone else will have to chip in to say if the theorem guarantees any certain upper boundary that could be used to allow using the simple sieve without having to worry about growing it. iirc that's sadly not the case.
As already mentioned, the presented algorithm cannot be improved significantly. If a fast solution is requested then the Eratosthenes sieve is appropriate. The size x of the sieve can be estimated using n >= x/(ln x + 2) if x >= 55. This equation can be solved using the Newton's iteration. The presented algorithm is about 10 times faster the original:
def sieveSize(n):
# computes x such that pi(x) >= n (assumes x >= 55)
x = 1.5 * n # start
y = x - n * math.log(x) - 2 * n
while abs(y) > 0.1:
derivative = 1 - n/x
x = x - y / derivative
y = x - n * math.log(x) - 2 * n
return int(x) + 1
def eratosthenes(n):
# create a string flags: flags[i]=='1' iff i prime
size = sieveSize(n)
flags = ['1'] * size # start with: all numbers are prime
flags[0] = flags[1] = '0' # 0 and 1 are not primes
i = 0
while i * i < size:
if flags[i] == '1':
for j in range(i * i, size, i):
flags[j] = '0'
i += 1
return flags
def primes(n):
flags = eratosthenes(n)
prims = []
for i in range(0, len(flags)):
if flags[i] == '1':
prims.append(i)
return prims
prims = primes(100000)
Any number that ends in 5, other than 5, is not a prime. So you can put a statement that skips any number ending in 5 that is greater than 5.