I'm trying to make the fastest algorithm to find out if a number is prime.
I'm doing this for each number from 3 to 100,000.
for(int i = 3; i < 100000; i += 1)
if(isPrime(i))
System.out.println(i);
and it takes 0.52 seconds.
My friend suggested to not iterate for even numbers:
for(int i = 3; i < 100000; i += 2)
if(isPrime(i))
System.out.println(i);
and it takes 0.53 seconds (probably a random difference).
Why doesn't his suggestion reduce the runtime?
if I iterate through less numbers I'd expect that the program will run faster.
Code of isPrime():
public static boolean isPrime(int n)
{
if((n % 2 == 0 && n != 2) || (n % 3 == 0 && n != 3)|| (n % 5 == 0 && n != 5))
return false;
for(int i = 5; i < n / 5; i += 2)
{
if(n % i == 0)
return false;
}
return true;
}
Most probably the bulk of the time is needed for printing out the primes to the console. Thus reducing the numbers tested won't notably affect the speed of the program, if the number of primes is not reduced as well.
Try to collect the primes into a string and print that out once, like this:
StringBuilder b = new StringBuilder();
for(int i = 3; i < 100000; i += 1)
if(isPrime(i))
b.append(i).append("\n");
System.out.println(b.toString());
Apart from algorithmical issues, your bottleneck is most likely writing to the System.out stream. That is a time-consuming task, I think you should leave that part out of the benchmark loop. To quickly test this, just comment it out (with the corresponding if statement):
int iterations=100000;
long time = System.nanoTime();
for(int i = 3; i < 100000; i += 2) { //notice: always use curly brackets!!!
isPrime(i);
}
long endTime = System.nanoTime();
System.out.println("Time to go through " + iterations + " iterations: " + (endTime>time?endTime-time:endTime+time));
//notice: nanoTime might turn around, resulting in smaller (negative) endTime value
Also, the answer of Thomas is much more detailed regarding the System.out.print issue, and also provides the appropriate approach of concatenating a lot of strings.
Algorithmic problems:
If you are going with a sieve of Erastothenes approach, you already have found all the smaller primes when searching for the next one. So you should store them, and instead of checking each and every odd number >5, you only have tocheck those that you already have.
Also, at the same time, you don't have to check all of them, only those, that are smaller than, or equal to the square root of your number:
//suppose we have a List<Integer> primeList (populated by previous execution loops)
// and Integer numberTested as the number under testing
for(int i=0; i<primeList.size();i++) {
if(numberTested%primeList.get(i)==0) {
//divider found, not prime
break;
}
if(primeList.get(i)>Math.sqrt(numberTested)) {
//still not found a divider -- we found a prime
primeList.add(numberTested);
break;
}
}
What is the problem with this? Math.sqrt is a costly operation. Much costlier than multiplication... So if the range of numbers permits (you have to have that always in mind!), we can use multiplication to get the comparison quicker:
sqrt(a)>b === a*a>b given that both a and b are positive integers.
Integer currentPrimeSquare = primeList.get(i)*primeList.get(i);
if(currentPrimeSquare>numberTested) {
//still not found a divider -- we found a prime
primeList.add(numberTested);
break;
}
To further tune this, you could store the squares of the primes too - given you have enough memory....
There is the overhead of VM itself to worry about when comparing java runtimes (println can take different periods at every execution for instance). To get a proper comparison of runtimes, run each one a thousand times and get average values for more accurate results.
Also, implementing the Sieve of Erastothenes in Java is not that difficult an will give you very quick results too, though it will use more memory.
If you're interested in the most efficient algorithm known to date whether a given number is prime, you should consider reading about the AKS primality test, which runs in polylogarithmic time.
(This answer basically is a reiteration of another answer to a question regarding primality tests.)
1) did you consider to use Sieve of Eratosthenes for finding primes?
2) you shouldn't really measure efficiency by using running time
3) your if statement in is prime, if you are checking is n = {2,3,5} use it to return result, that will prevent you from executing rest of code (not much time saver in this case)
4) your for loop, you can do it for i from 7 (condition for 5 you already check, and 6 is not prime number) to sqrt(n)
Maybe this article can help you:
boolean isPrime(int n) {
//check if n is a multiple of 2
if (n%2==0) return false;
//if not, then just check the odds
for(int i=3;i*i<=n;i+=2) {
if(n%i==0)
return false;
}
return true;
}
Related
I was looking over this on YouTube and saw some code. I got most of it but didn't understand at all the reason why you the value n divide by two. Could someone please explain it?
import java.util.ArrayList;
public class Primes {
public static void main(String[] args) {
System.out.println(findPrimes(1, 100));
}
public static ArrayList<Integer> findPrimes(int start, int end) {
ArrayList<Integer> primes = new ArrayList<Integer>();
for(int n = start; n < end; n++) {
boolean prime = true;
int i = 2;
while(i <= n/2) {
if(n % i == 0) {
prime = false;
break;
}
i++;
}
if(prime) {
primes.add(n);
}
}
return primes;
}
}
A number can only be divided by a number that is less than or equal to its half. Or the number to be divided itself.
Here, we are checking till n/2 so that the number of iterations and the load on the system reduces and make the program more efficient. Since the greatest factor which can divide a number (excluding itself) is half of the number, we check only till that point. There is no point of checking beyond that.
It's an attempt to improve the efficiency of the solution by stopping the loop at a point where the algorithm will no longer find any more prime factors.
However, it's suboptimal. If n == a * b and a <= b, a can be no higher than the square root of n. Therefore, checking for i <= Math.sqrt(n) would eliminate many more unnecessary iterations of the loop for large values of n.
A further improvement could be made by iterating over known values of primes instead of incremental values of i, since once you've established that a number isn't divisible by 3 there's no point checking 6, 9, 12 and so on.
I am attempting to solve a challenge, but I have hit a roadblock. I am a beginner programmer attempting to add tens of thousands of numbers. If I wait long enough, my program can easily yield the correct sum, however, I am looking for a more efficient method.
What is an efficient method for adding thousands of numbers quickly?
Side note: I have been reading about modular arithmetic, but I cannot quite wrap my head around it. Not sure if that could be useful for this situation.
I am attempting to get the sum of every prime number below 2 000 000. Here is my code so far:
public class Problem10 {
public static void main (String[] args) {
long sum = 0L;
for(long i = 1L; i < 2000000; i++) {
if(isPrimeNumber((int)i)) {
sum += i;
}
}
System.out.println(sum);
}
public static boolean isPrimeNumber(int i) {
int factors = 0;
int j = 1;
while (j <= i) {
if (i % j == 0) {
factors++;
}
j++;
}
return (factors == 2);
}
}
You can replace your isPrimeNumber() method with this to speed it up substantially.
public static boolean isPrimeNumber(int i) {
if (i==2) return true;
if (i==3) return true;
if (i%2==0) return false;
if (i%3==0) return false;
int j = 5;
int k = 2;
while (j * j <= i) {
if (i % j == 0) return false;
j += k ;
k = 6 - k;
}
return true;
}
This looks like homework, so I'm not going to give you the solution in code. The least you can do is code it yourself.
In your code, isPrimeNumber() is what's taking up most of the timeāif I had to guess, I would say 90-99% of it. What you can do to make it faster is implement the Sieve of Eratosthenes.
To start, you create an array that will hold all the prime numbers1. You should start it with a single value: 2. To find more prime numbers, iterate through every integer from 3 to the highest number you want. For each of those numbers, check if that number is divisible by any of the prime numbers in your array. If the next prime number in your array is greater than i / 2, you know that i is prime, and you can add it to your array.
After you have found all the prime numbers from 1 to n, the only way to sum them is by iterating through the array. That part cannot be optimized, but it will not take very long anyways.
1 There are two ways to do this. One is to just use an ArrayList or LinkedList, and add numbers as needed. The other is to create an array that is as large or larger than you need. As mentioned here, the number of primes equal to or less than n is less than (n / log(n)) * (1 + 1.2762 / log(n)), as long as n is greater than 598. If n is less than 598, you can just create an array of length 109.
In regards to the question in the title, "What is an efficient method for adding thousands of numbers quickly?", the only thing I can think of is multithreading. Create an array of all the numbers you want to sum, then have many threads sum different parts of the array. After that, sum all the results from each thread. This method will probably only be noticeably faster when summing huge amount of numbers, e.g. hundreds of thousands or millions.
This question already has answers here:
Project Euler #10 Java solution not working
(6 answers)
Closed 9 years ago.
I'm trying to find the sum of primes below millions. My code works when I try to find the sum of primes below hundred thousands but when I go large numbers it doesn't work. So I need some help to get this work for big numbers...
import java.util.Scanner;
public class sumPrime {
public static void main (String args []){
long n = 2000000; int i; int j;int sum =0;
for (i=2; i <n; i++){
for (j=2; j<i; j++){
if (i%j==0){
break;
}
}
if (i==j){
sum +=i;
}
}
System.out.print(sum);
}
}
Your code could be improved by making the inner loop stop earlier. If a number N is not prime, then it must have at least one factor (apart from 1) that is less or equal to sqrt(N). In this case, this simple change should make the program roughly 1000 times faster.
For a simple and (more) efficient algorithm, read up on the Sieve of Eratosthenes.
Bug - your sum needs to be a long. An int will probably overflow.
Note that the classic formulation of Sieve of Eratosthenes needs a large array of booleans (or a bitmap) whose size depends on the largest prime candidate you are interested in. In this case that means a 2Mbyte array (or smaller if you use a bitmap) ... which is too small to worry about. Also, you can reduce the memory usage by sieving in stages, though it makes the code more complicated.
Rather than trying to divide by all the numbers below i you could potentially keep the found prime numbers in a list and try to divide by those prime numbers (since any non prime number will be divisible by a prime number less than that).
public static long sumPrime2() {
List<Long> primes = new ArrayList<>();
primes.add(2L);
primes.add(3L);
long primeSum = 5;
for (long primeCandidate = 5; primeCandidate < 2000000; primeCandidate = primeCandidate + 2) {
boolean isCandidatePrime = true;
double sqrt = Math.sqrt(primeCandidate);
for (int i = 0; i < primes.size(); i++) {
Long prime = primes.get(i);
if (primeCandidate % prime == 0) {
isCandidatePrime = false;
break;
}
if (prime > sqrt) {
break;
}
}
if (isCandidatePrime) {
primes.add(primeCandidate);
primeSum += primeCandidate;
}
System.out.println(primeCandidate);
}
System.out.println(primes.size());
return primeSum;
}
This gave the answer in 8 seconds
I suspect integer overflow in i, j, sum - try making them all longs. In the sample code you shouldn't be getting overflows as Java ints are meant to be 32 bit but at some stage you certainly will.
As already mentioned - i only needs to iterate to the square root of n. So I would replace this line:
for (i=2; i <n; i++){
With:
long limit=sqrt(n);
for (i=2; i <limit; i++){
Note that calculating the square root outside the program loops will also speed things up a bit.
Also the sieve algorithm would be faster but requires Java to create an array containing n elements and at some stage that is going to fail with insufficient memory.
The best algorithm for this program uses the Sieve of Eratosthenes:
function sumPrimes(n)
sum, sieve := 0, makeArray(2..n, True)
for p from 2 to n
if sieve[p]
sum := sum + p
for i from p*p to n step p
sieve[i] := False
return sum
Then sumPrimes(2000000) returns the sum of the primes less than two million, in about a second. I'll leave it to you to translate to Java, with an appropriate data type for the sum. If you're interested in programming with prime numbers, I modestly recommend this essay at my blog.
Problem 10 from Project Euler:
The program runs for smaller numbers and slows to a crawl in the hundred thousands.
At 2 million, an answer fails to show up even though the program seems like it is still running.
I'm trying to implement the Sieve of Eratosthenes. It is supposed to be very fast. What's wrong with my approach?
import java.util.ArrayList;
public class p010
{
/**
* The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17
* Find the sum of all the primes below two million.
* #param args
*/
public static void main(String[] args)
{
ArrayList<Integer> primes = new ArrayList<Integer>();
int upper = 2000000;
for (int i = 2; i < upper; i++)
{
primes.add(i);
}
int sum = 0;
for (int i = 0; i < primes.size(); i++)
{
if (isPrime(primes.get(i)))
{
for (int k = 2; k*primes.get(i) < upper; k++)
{
if (primes.contains(k*primes.get(i)))
{
primes.remove(primes.indexOf(k*primes.get(i)));
}
}
}
}
for (int i = 0; i < primes.size(); i++)
{
sum += primes.get(i);
}
System.out.println(sum);
}
public static boolean isPrime(int number)
{
boolean returnVal = true;
for (int i = 2; i <= Math.sqrt(number); i ++)
{
if (number % i == 0)
{
returnVal = false;
}
}
return returnVal;
}
}
You appear to be trying to implement the Sieve of Eratosthenes which should perform better that O(N^2) (In fact, Wikipedia says it is O(N log(log N)) ...).
The fundamental problem is your choice of data structure. You've chosen to represent the set of remaining prime candidates as an ArrayList of primes. This means that your test to see if a number is still in the set takes O(N) comparisons ... where N is the number of remaining primes. Then you are using ArrayList.remove(int) to remove the non-primes ... which is O(N) also.
That all adds up to making your Sieve implementation worse than O(N^2).
The solution is to replace the ArrayList<Integer> with an boolean[] where the positions (indexes) in the boolean array represent the numbers, and the value of the boolean says whether the number is prime / possibly prime, or not prime.
(There were other problems too that I didn't notice ... see the other answers.)
There are a few issues here. First, lets talk about the algorithm. Your isPrime method is actually the very thing that the sieve is designed to avoid. When you get to a number in the sieve, you already know it's prime, you don't need to test it. If it weren't prime, it would already have been eliminated as a factor of a lower number.
So, point 1:
You can eliminate the isPrime method altogether. It should never return false.
Then, there are implementation issues. primes.contains and primes.remove are problems. They run in linear time on an ArrayList, because they require checking each element or rewriting a large portion of the backing array.
Point 2:
Either mark values in place (use boolean[], or use some other more appropriate data structure.)
I typically use something like boolean primes = new boolean[upper+1], and define n to be included if !(primes[n]). (I just ignore elements 0 and 1 so I don't have to subtract indices.) To "remove" an element, I set it to true. You could also use something like TreeSet<Integer>, I suppose. Using boolean[], the method is near-instantaneous.
Point 3:
sum needs to be a long. The answer (roughly 1.429e11) is larger than the maximum value of an integer (2^31-1)
I can post working code if you like, but here's a test output, without spoilers:
public static void main(String[] args) {
long value;
long start;
long finish;
start = System.nanoTime();
value = arrayMethod(2000000);
finish = System.nanoTime();
System.out.printf("Value: %.3e, time: %4d ms\n", (double)value, (finish-start)/1000000);
start = System.nanoTime();
value = treeMethod(2000000);
finish = System.nanoTime();
System.out.printf("Value: %.3e, time: %4d ms\n", (double)value, (finish-start)/1000000);
}
output:
Using boolean[]
Value: 1.429e+11, time: 17 ms
Using TreeSet<Integer>
Value: 1.429e+11, time: 4869 ms
Edit:
Since spoilers are posted, here's my code:
public static long arrayMethod(int upper) {
boolean[] primes = new boolean[upper+1];
long sum = 0;
for (int i = 2; i <=upper; i++) {
if (!primes[i]) {
sum += i;
for (int k = 2*i; k <= upper; k+=i) {
primes[k] = true;
}
}
}
return sum;
}
public static long treeMethod(int upper) {
TreeSet<Integer> primes = new TreeSet<Integer>();
for (int i = 2; i <= upper; i++) {
primes.add(i);
}
long sum = 0;
for (Integer i = 2; i != null; i=primes.higher(i)) {
sum += i;
for (int k = 2*i; k <= upper; k+=i) {
primes.remove(k);
}
}
return sum;
}
Two things:
Your code is hard to follow. You have a list called "primes", that contains non prime numbers!
Also, you should strongly consider whether or not an array list is appropriate. In this case, a LinkedList would be much more efficient.
Why is this? An array list must constantly resize an array by: asking for new memory to create an array, then copying the old memory over in the newly created array. A Linked list would just resize the memory by changing a pointer. This is a lot quicker! However, I do not think that by making this change you can salvage your algorithm.
You should use an array list if you need to access the items non-sequentially, here, (with a suitable algorithm) you need to access the items sequentially.
Also, your algorithm is slow.Take the advice of SJuan76 (or gyrogearless), thanks sjuan76
The key to the efficiency of classic implementation of the sieve of Eratosthenes on modern CPUs is the direct (i.e. non-sequential) memory access. Fortunately, ArrayList<E> does implement RandomAccess.
Another key to the sieve's efficiency is its conflation of index and value, just like in integer sorting. Actually removing any number from the sequence destroys this ability to directly address without any computations. We must mark, not remove, any composite as we find them, so any numbers greater than it will remain in their places in the sequence.
ArrayList<Integer> can be used for that (except taking more memory than is strictly necessary, but for 2 million this is inconsequential).
So your code with a minimal edit fix (also changing sum to be long as others point out too), becomes
import java.util.ArrayList;
public class Main
{
/**
* The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17
* Find the sum of all the primes below two million.
* #param args
*/
public static void main(String[] args)
{
ArrayList<Integer> primes = new ArrayList<Integer>();
int upper = 5000;
primes.ensureCapacity(upper);
for (int i = 0; i < upper; i++) {
primes.add(i);
}
long sum = 0;
for (int i = 2; i <= upper / i; i++) {
if ( primes.get(i) > 0 ) {
for (int k = i*i; k < upper ; k+=i) {
primes.set(k, 0);
}
}
}
for (int i = 2; i < upper; i++) {
sum += primes.get(i);
}
System.out.println(sum);
}
}
Finds the result for 2000000 in half a second on Ideone. The projected run time for your original code there: between 10 and 400 hours (!).
To find rough estimates for the run time when faced with a slow code, you should always try to find out its empirical orders of growth: run it for some small size n1, then a bigger size n2, record the run times t1 and t2. If t ~ n^a, then a = log(t2/t1) / log(n2/n1).
For your original code the empirical orders of growth measured on 10k .. 20k .. 40k range of upper limit value N, are ~ N^1.7 .. N^1.9 .. N^2.1. For the fixed code it's faster than ~ N (in fact, it's ~ N^0.9 in the tested range 0.5 mln .. 1 mln .. 2 mln). The theoretical complexity is O(N log (log N)).
Your program is not the Sieve of Eratosthenes; the modulo operator gives it away. Your program will be O(n^2), where a proper Sieve of Eratosthenes is O(n log log n), which is essentially n. Here's my version; I'll leave it to you to translate to Java with appropriate numeric datatypes:
function sumPrimes(n)
sum := 0
sieve := makeArray(2..n, True)
for p from 2 to n step 1
if sieve[p]
sum := sum + p
for i from p * p to n step p
sieve[i] := False
return sum
If you're interested in programming with prime numbers, I modestly recommend this essay at my blog.
In the following snippet from https://github.com/nayuki/Project-Euler-solutions/blob/master/p003.java :
private static long smallestFactor(long n) {
for (long i = 2, end = Library.sqrt(n); i <= end; i++) {
if (n % i == 0)
return i;
}
return n; // Prime
}
I was a bit confused with the return n part. Is n going to assume the value of i after it's returned in the if statement? Why?
No. It returns the unchanged parameter to indicate that it is prime.
If it is not prime, it returns the factor that shows it is not prime.
lets take a num.
num is divisible upto its half (i.e factors)
Implement Sieve of Eratosthenes for finding prime_numbers. It's an efficient process for finding Prime numbers. And then check whether the returned primes divide the "num" or not...