Prime Number Calculator Not Working With Large Numbers - java

I am trying to print out all the prime factors of a number. My code is as follows:
public static boolean isPrime(long n){
long i = n;
while (i > 0){
if (n % i == 0 && !(i == 1 || i == n)){
return false;
}
i--;
}
return true;
}
public static void primeFactors(long n){
long i = n;
while (i > 0){
if (isPrime(i)){
System.out.println(i);
}
i--;
}
}
This code works for small numbers: 5, 5000,e.g. When I input 600851475143 to the method, my program runs and nothing is output. Why is this happening?

Your primality test function is horrible.
Quick win: count forwards not backwards. Currently you'll count through at lease half your number until you find a factor! That probably accounts for the delay you are observing.
Bit better: count odd numbers up to the square root.
Perhaps better still: count prime numbers up to the square root. precompute those using a sieve depending on the requirements.

The other answers have focused on your function isPrime, which is inefficient. I will instead focus on your function primeFactors which is incorrect. Indeed, you can see that it prints primes up to n in reverse order, rather than the prime factors of n. Instead, do
public static void primeFactors(long n) {
// Handle factors of 2
while (n%2==0) {
System.out.println(2);
n /= 2;
}
// Handle all odd prime factors except the largest
for (long p = 3; p*p <= n; p += 2) {
while (n%p == 0) {
System.out.println(p);
n /= p;
}
}
// Handle the largest prime factor
if (n > 1) {
System.out.println(2);
}
}
You might notice that isPrime is never used! In fact it is not needed: as long as they are removed as you find them, all of the numbers you print will be primes.
There are many possible improvements here, but this method is fast enough as-is. One simple way would be to compute an upper bound of Math.sqrt(n) and compare p to it rather than multiplying p by itself each time. You might also want to check for 0 and negative numbers, and possibly 1, depending on how you want to handle those numbers.

When given the input number 600851475143, your program is printing nothing because the way you check for prime numbers is slow. Given enough patience, your program will print something - but this could be hours, days, even years. You would need to come up with a better algorithm for this calculation.

Short answer: the program actually isn't terminating. The method that you are using is one of the most inefficient ways to check for primes. Check out the Sieve of Eratosthenes for an easy to understand, fairly efficient algorithm.

no need to check n numbers to check if n is prime or no, It's enough to start from 0 to n/2:
public static boolean isPrime(long n){
long i = 2;
while (i < n/2){
if (n % i == 0 ){
return false;
}
i++;
}
return true;
}
This will double performance

Although the number is not out of long primitive value range (9,223,372,036,854,775,807), you should use BigInteger since BigInteger has function for this purpose and I think it is more efficient then other implementations including yours.
new BigInteger(yourNumer).isProbablePrime(100) // returns true or false
Furthermore primality test is done by using many mathematical approaches and algorithms, there are some ways which runs faster for numbers < 64bit and there are some which are better to test much larger numbers. There some other ways here

Change your method to the following:
public static boolean isPrime(long num) {
for (int i = 2; i < Math.sqrt(num); i++) {
if (num % i == 0) {
return false;
}
}
return true;
}
Should be efficient enough.

Related

How to find the 5th perfect number (which is 33550336)? The problem is taking forever to run

I am trying to write a Java method that checks whether a number is a perfect number or not.
A perfect number is a number that is equal to the sum of all its divisor (excluding itself).
For example, 6 is a perfect number because 1+2+3=6. Then, I have to write a Java program to use the method to display the first 5 perfect numbers.
I have no problem with this EXCEPT that it is taking forever to get the 5th perfect number which is 33550336.
I am aware that this is because of the for loop in my isPerfectNumber() method. However, I am very new to coding and I do not know how to come up with a better code.
public class Labreport2q1 {
public static void main(String[] args) {
//Display the 5 first perfect numbers
int counter = 0,
i = 0;
while (counter != 5) {
i++;
isPerfectNumber(i);
if (isPerfectNumber(i)) {
counter++;
System.out.println(i + " ");
}
}
}
public static boolean isPerfectNumber(int a) {
int divisor = 0;
int sum = 0;
for (int i = 1; i < a; i++) {
if (a % i == 0) {
divisor = i;
sum += divisor;
}
}
return sum == a;
}
}
This is the output that is missing the 5th perfect number
Let's check the properties of a perfect number. This Math Overflow question tells us two very interesting things:
A perfect number is never a perfect square.
A perfect number is of the form (2k-1)×(2k-1).
The 2nd point is very interesting because it reduces our search field to barely nothing. An int in Java is 32 bits. And here we see a direct correlation between powers and bit positions. Thanks to this, instead of making millions and millions of calls to isPerfectNumber, we will be making less than 32 to find the 5th perfect number.
So we can already change the search field, that's your main loop.
int count = 0;
for (int k = 1; count < 5; k++) {
// Compute candidates based on the formula.
int candidate = (1L << (k - 1)) * ((1L << k) - 1);
// Only test candidates, not all the numbers.
if (isPerfectNumber(candidate)) {
count++;
System.out.println(candidate);
}
}
This here is our big win. No other optimization will beat this: why test for 33 million numbers, when you can test less than 100?
But even though we have a tremendous improvement, your application as a whole can still be improved, namely your method isPerfectNumber(int).
Currently, you are still testing way too many numbers. A perfect number is the sum of all proper divisors. So if d divides n, n/d also divides n. And you can add both divisors at once. But the beauty is that you can stop at sqrt(n), because sqrt(n)*sqrt(n) = n, mathematically speaking. So instead of testing n divisors, you will only test sqrt(n) divisors.
Also, this means that you have to start thinking about corner cases. The corner cases are 1 and sqrt(n):
1 is a corner case because you if you divide n by 1, you get n but you don't add n to check if n is a perfect number. You only add 1. So we'll probably start our sum with 1 just to avoid too many ifs.
sqrt(n) is a corner case because we'd have to check whether sqrt(n) is an integer or not and it's tedious. BUT the Math Overflow question I referenced says that no perfect number is a perfect square, so that eases our loop condition.
Then, if at some point sum becomes greater than n, we can stop. The sum of proper divisors being greater than n indicates that n is abundant, and therefore not perfect. It's a small improvement, but a lot of candidates are actually abundant. So you'll probably save a few cycles if you keep it.
Finally, we have to take care of a slight issue: the number 1 as candidate. 1 is the first candidate, and will pass all our tests, so we have to make a special case for it. We'll add that test at the start of the method.
We can now write the method as follow:
static boolean isPerfectNumber(int n) {
// 1 would pass the rest because it has everything of a perfect number
// except that its only divisor is itself, and we need at least 2 divisors.
if (n < 2) return false;
// divisor 1 is such a corner case that it's very easy to handle:
// just start the sum with it already.
int sum = 1;
// We can stop the divisors at sqrt(n), but this is floored.
int sqrt = (int)Math.sqrt(n);
// A perfect number is never a square.
// It's useful to make this test here if we take the function
// without the context of the sparse candidates, because we
// might get some weird results if this method is simply
// copy-pasted and tested on all numbers.
// This condition can be removed in the final program because we
// know that no numbers of the form indicated above is a square.
if (sqrt * sqrt == n) {
return false;
}
// Since sqrt is floored, some values can still be interesting.
// For instance if you take n = 6, floor(sqrt(n)) = 2, and
// 2 is a proper divisor of 6, so we must keep it, we do it by
// using the <= operator.
// Also, sqrt * sqrt != n, so we can safely loop to sqrt
for (int div = 2; div <= sqrt; div++) {
if (n % div == 0) {
// Add both the divisor and n / divisor.
sum += div + n / div;
// Early fail if the number is abundant.
if (sum > n) return false;
}
}
return n == sum;
}
These are such optimizations that you can even find the 7th perfect number under a second, on the condition that you adapt the code for longs instead of ints. And you could still find the 8th within 30 seconds.
So here's that program (test it online). I removed the comments as the explanations are here above.
public class Main {
public static void main(String[] args) {
int count = 0;
for (int k = 1; count < 8; k++) {
long candidate = (1L << (k - 1)) * ((1L << k) - 1);
if (isPerfectNumber(candidate)) {
count++;
System.out.println(candidate);
}
}
}
static boolean isPerfectNumber(long n) {
if (n < 2) return false;
long sum = 1;
long sqrt = (long)Math.sqrt(n);
for (long div = 2; div <= sqrt; div++) {
if (n % div == 0) {
sum += div + n / div;
if (sum > n) return false;
}
}
return n == sum;
}
}
The result of the above program is the list of the first 8 perfect numbers:
6
28
496
8128
33550336
8589869056
137438691328
2305843008139952128
You can find further optimization, notably in the search if you check whether 2k-1 is prime or not as Eran says in their answer, but given that we have less than 100 candidates for longs, I don't find it useful to potentially gain a few milliseconds because computing primes can also be expensive in this program. If you want to check for bigger perfect primes, it makes sense, but here? No: it adds complexity and I tried to keep these optimization rather simple and straight to the point.
There are some heuristics to break early from the loops, but finding the 5th perfect number still took me several minutes (I tried similar heuristics to those suggested in the other answers).
However, you can rely on Euler's proof that all even perfect numbers (and it is still unknown if there are any odd perfect numbers) are of the form:
2i-1(2i-1)
where both i and 2i-1 must be prime.
Therefore, you can write the following loop to find the first 5 perfect numbers very quickly:
int counter = 0,
i = 0;
while (counter != 5) {
i++;
if (isPrime (i)) {
if (isPrime ((int) (Math.pow (2, i) - 1))) {
System.out.println ((int) (Math.pow (2, i -1) * (Math.pow (2, i) - 1)));
counter++;
}
}
}
Output:
6
28
496
8128
33550336
You can read more about it here.
If you switch from int to long, you can use this loop to find the first 7 perfect numbers very quickly:
6
28
496
8128
33550336
8589869056
137438691328
The isPrime method I'm using is:
public static boolean isPrime (int a)
{
if (a == 1)
return false;
else if (a < 3)
return true;
else {
for (int i = 2; i * i <= a; i++) {
if (a % i == 0)
return false;
}
}
return true;
}

Returning String of prime factors in Java

I know this is a classic problem. I solved it in Java. I have my solution below. However, when I used this solution in codefights.com, It went beyond the execution time limit. I would appreciate if anyone could give me suggestions on improving this code in any way possible. Please feel free to criticize my code so that I can improve on my coding skills. Thanks
You're given number n.
Return n as a product of its prime factors.
Example
For n = 22 the output should be "2*11".
For n = 120 the output should be "2*2*2*3*5".
For n = 17194016 the output should be "2*2*2*2*2*7*59*1301".
[input] integer n
An integer number smaller than 109. [output] string
The Prime factors of n which splitted by * symbol. prime factors
should be in increasing order.
Solution (JAVA):
public String primefactors(int n) {
String factors = "";
for (int i = 2; i <= n / 2; i++) {
if (isPrime(i)) {
while (n % i == 0) {
n /= i;
if (isPrime(n) && n != 1) {
factors = factors + Integer.valueOf(i).toString() + "*"
+ Integer.valueOf(n).toString();
break;
} else if (n == 1)
factors = factors + Integer.valueOf(i).toString();
else
factors = factors + Integer.valueOf(i).toString() + "*";
}
}
}
return factors;
}
public boolean isPrime(int n) {
boolean prime = true;
if (n == 1)
return false;
else if (n % 2 == 0 && n!=2)
return false;
else if (n % 3 == 0 && n!=3)
return false;
else {
for (int j = 2; j < n / 2; j++) {
if (n % j == 0) {
return false;
}
}
}
return prime;
}
Since n is smaller than a fixed number (109), simply use a table containing all prims <= 109, instead of generating them dynamically. Or atleast generate the prims first using the sieve of erathostenes or atkin. The hardcoded table would be preferable, but using a sieve for generating the table dynamically would aswell speed things up alot. The isPrime() function you implemented is a performance killer.
The function isPrime() is called too much times in primefactors. For example, i == 2 and there are many divisors 2 in n. The top call (isPrime(i)) is fine. However, inside the loop while (n % i == 0) you check isPrime(n) after each dividing n /= 2;. So, if initial n is 100 the function isPrime() is called for 50 and on the next loop for 25. That does not make any sense. I think that it is the biggest problem here, since even if isPrime works in linear time it is too much to call it many times in the internal loop.
It is possible to exit from the loop for i in two cases: n is equal 1 after divisions or n is for sure prime if i is larger than sqrt(n).
public String primefactors(int n) {
String factors = "";
int max_divisor = sqrt(n);
for (int i = 2; i <= max_divisor; i++) {
if (isPrime(i)) {
while (n % i == 0) {
n /= i;
if (n == 1)
factors = factors + Integer.valueOf(i).toString();
else
factors = factors + Integer.valueOf(i).toString() + "*";
}
max_divisor = sqrt(n);
}
}
// check for the last prime divisor
if (n != 1)
factors = factors + Integer.valueOf(n).toString();
return factors;
}
Even after that improvement (and sqrt(n) as the max limit in isPrime()) your algorithm will have linear complexity O(n), since there are at most sqrt(n) loops for i and the maximum number of probes for prime in isPrime is also sqrt(n).
Yes, it can be done better by choosing better algorithms for isPrime(). Even if you are not allowed to use hardcoded table of prime numbers it is possible to generate such look up table in runtime (if there are enough memory). So, it is possible to use automatically generated list of primes organized in ascending order to probe given number up to sqrt(n). If i becomes larger than sqrt(n) it means that the next prime is found and it should be appended to the look up table and isPrime() should return true.
Example
Suppose isPrime is called for 113. At that moment the look up table has a list of previous prime numbers: 2,3,5,7,11,13.... So, we try to divide 113 by items from that list up to sqrt(113) (while (i <= 10)). After trying 2,3,5,7 the next item on the list 11 is too large, so 113 is appended to the list of primes and the function returns true.
The other algorithms may give better performance in the worst case. For example the sieve of Eratosthenes or sieve of Atkin can be used to effectively precomputed list of prime numbers up to given n with the best O(n) complexity for the best implementation. Here you need to find all primes up to sqrt(n), so it takes O(sqrt(n)) to generate such list. Once such list is generated you need to try to divide your input by numbers is the list that takes at most sqrt(n) probes. So, the algorithm complexity is O(sqrt(n)). However, suppose that your input is 1024 that is 2 to the power of 10. In that case the first algorithm will be better, since it will not go to primes larger than 2.
Do you really need the function isPrime()?
With flexible thinking If we look closer it appears that you do not have to search for all primes in some range. You only needed to find all prime divisors of one given integer. However if we try to divide n by all integers in range up to sqrt(n) that is also good solution. Even if such integer is not prime it will be skipped due to the condition n % i == 0, since all primes lower than the integer under test are already removed from n, so that simple modular division does here the same as isPrime(). The full solution with O(sqrt(n)) complexity:
public String primefactors(int n) {
String factors = "";
int max_divisor = sqrt(n);
for (int i = 2; i <= max_divisor; i++) {
while (n % i == 0) {
n /= i;
max_divisor = sqrt(n);
if (n == 1)
factors = factors + Integer.valueOf(i).toString();
else
factors = factors + Integer.valueOf(i).toString() + "*";
}
}
// check for the last prime divisor
if (n != 1)
factors = factors + Integer.valueOf(n).toString();
return factors;
}
It is also possible to split the function to avoid if (n == 1) check in the inner loop, however it does not change the algorithm complexity.

Russian Doll Primes

This question was asked in an interview (about prime numbers)
Russian Doll Primes
They are more commonly known as Truncatable Primes.
I found this code on wiki
public static void main(String[] args){
final int MAX = 1000000;
//Sieve of Eratosthenes (using BitSet only for odd numbers)
BitSet primeList = new BitSet(MAX>>1);
primeList.set(0,primeList.size(),true);
int sqroot = (int) Math.sqrt(MAX);
primeList.clear(0);
for(int num = 3; num <= sqroot; num+=2)
{
if( primeList.get(num >> 1) )
{
int inc = num << 1;
for(int factor = num * num; factor < MAX; factor += inc)
{
//if( ((factor) & 1) == 1)
//{
primeList.clear(factor >> 1);
//}
}
}
}
//Find Largest Truncatable Prime. (so we start from 1000000 - 1
int rightTrunc = -1, leftTrunc = -1;
for(int prime = (MAX - 1) | 1; prime >= 3; prime -= 2)
{
if(primeList.get(prime>>1))
{
//Already found Right Truncatable Prime?
if(rightTrunc == -1)
{
int right = prime;
while(right > 0 && primeList.get(right >> 1)) right /= 10;
if(right == 0) rightTrunc = prime;
}
//Already found Left Truncatable Prime?
if(leftTrunc == -1 )
{
//Left Truncation
String left = Integer.toString(prime);
if(!left.contains("0"))
{
while( left.length() > 0 ){
int iLeft = Integer.parseInt(left);
if(!primeList.get( iLeft >> 1)) break;
left = left.substring(1);
}
if(left.length() == 0) leftTrunc = prime;
}
}
if(leftTrunc != -1 && rightTrunc != -1) //Found both? then Stop loop
{
break;
}
}
}
System.out.println("Left Truncatable : " + leftTrunc);
System.out.println("Right Truncatable : " + rightTrunc);
}
This gives the output:
Left Truncatable : 998443
Right Truncatable : 796339
But I am not able to solve this particular Russian doll prime number problem like if you have a prime number and you remove either left or right digit of this prime number then if that resulting number is prime number or not?
I am new to this so please pardon any mistake.
Let's start from the beginning:
According to the link you supplied with your question:
"Russian Doll Primes are
prime numbers whose right digit can be repeatedly removed, and are
still prime."
I will assume that you have a function boolean isPrime(int) to find out if a number is prime.
Googling, we will find from Wikipedia that the number of right-truncatable prime numbers up to 73,939,133 is 83, which makes brute-force a viable option; but a few optimization techniques can be employed here:
Since we right-truncate, we can safely eliminate even numbers (since any even number won't be prime, and so any number generated upon it will never be a russian doll prime).
Since any number that starts with 5 is divisible by 5, then based on the same rule I mentioned in the previous point, we can eliminate 5.
That leaves us with numbers that contain 1, 3, 7, and 9.
Now if we wanted to generate all possible combinations of these 4 numbers that do not exceed the maximum you mentioned (1,000,000), it would take only 4,096 iterations.
The downside of this technique is that we now have 4,096 numbers that contain possible non-prime numbers, or prime numbers that are formed from non-prime numbers and hence are not russian doll primes. We can eliminate these numbers by looping through them and checking; or better yet, we can examine russian doll primes more closely.
Upon examining the rule I quoted from your link above, we find that a russian doll primes are prime numbers whose right digit can be repeatedly removed, and are still prime (and hence are still russian doll prime, given the word repeatedly)!
That means we can work from the smallest single-digit russian doll primes, work our generation magic that we used above, and since any prime number that is formed from russian doll prime numbers is a russian doll prime number, we can eliminate non-primes early on, resulting in a clean list of russian doll prime numbers, while reducing the running time of such a program dramatically.
Take a look at the generation code below:
void russianDollPrimesGeneration(int x) {
x *= 10;
if (x * 10 >= 1000000) return;
int j;
for (int i=1; i<=9; i+=2) {
if (i == 5) continue;
j = x + i;
if (isPrime(j)) {
addToRussianDollPrimesList(j);
russianDollPrimesGeneration(j);
}
}
}
Provided that void addToRussianDollPrimesList(int x) is a function that adds x to a list that we previously preserved to store the russian doll prime numbers.
UPDATED NOTE
Note that you can put the call to void russianDollPrimesGeneration(int x) that we made inside the if condition inside the void addToRussianDollPrimesList(int x) function, because whenever we call the former function, we will always call the latter function with the same arguments. I'm separating them here to emphasize the recursive nature of the generation function.
Also note that you must run this function with the integer 0.
A final note is that there are a number of cases that the generation function void russianDollPrimesGeneration(int x) above won't count, even though they are Russian Doll Primes.
Remember when we omitted 2 and 5, because even numbers and numbers divided by 5 cannot be primes and so cannot be Russian Doll Primes? and consequently cannot form Russian Doll Primes? Well, that case does not apply to 2 and 5, because they are prime, and since they are single digits, therefore they are Russian Doll Primes, and are eligible to form Russian Doll Primes, if placed in the left-side, like 23 and 53.
So how to correct our code to include these special cases?
We can make a wrapper function that adds these two numbers and checks for Russian Doll Primes that can be formed using them (which will be the same generation function we are using above).
void generationWrapperFunction(int x) {
addToRussianDollPrimesList(2);
russianDollPrimesGeneration(2);
addToRussianDollPrimesList(5);
russianDollPrimesGeneration(5);
russianDollPrimesGeneration(0);
}
END UPDATED NOTE
This little function will produce a list of russian doll prime numbers, which can then be searched for the number we are looking for.
An alternative, yet I believe will be more time-consuming, is the following recursive function:
boolean isRussianDollPrime(int n) {
if (!isPrime(n)) return false;
if (n < 10) return true;
return isRussianDollPrime(n / 10);
}
This function can be modified to work with left-truncatable primes. The generation-based solution, however, will be much difficult to implement for left-truncatable primes.
Your problem is to use this code or to solve the problem ?
if to solve it you can generate primes using Sieve algorithm then check if the element is prime or not (if it was prime then check if element/10 is also prime)
Let's start with a simple assumption that we know how to write code to detect if a value is a prime. In a coding interview, they won't likely won't expect you to pull out "Sieve of Eratosthenes". You should start with simple code that handles the special cases of x<=1 (false) and x==2(true). Then check for an even number !(x % 2)(false). Then loop on i from 3..sqrt(x) (incrementing by +2 each time) to see if there's an odd number divisor for x.
boolean isPrime(long x)
{
// your code goes here
}
And once we have a function to tell us if a value is prime, we can easily build the function to detect if a value is a Russian Prime. Therefore we just need to loop on our value, each time check for prime, and then chop off the right hand side. And the easiest way to remove the right-most digit from a number is to simply divide it by 10.
boolean isRussianPrime(long x)
{
boolean result = isPrime(x);
while ((x != 0) && result)
{
// chop off the right digit of x
x = x / 10;
if (x != 0)
{
result = isPrime(x);
}
}
return result;
}
And that's really all there is to it.
package com.example.tests;
public class RussianDollPrimeNumber {
public static void main(String[] args) {
int x= 373;
int k;
int n =x;
for ( k= String.valueOf(x).length()-1;k>0;k--){
System.out.println(n);
if (isPrime(n)){
String m=String.valueOf(n).substring(0, k);
n=Integer.parseInt(m);
continue;
}else {
break;
}
}
if( k==0){
System.out.println("Number is Russianl Doll Number "+x);
}else {
System.out.println("Number is not Russianl Doll Number "+x);
}
}
private static boolean isPrime(int x) {
boolean check=true;
for (int i=2;i<x/2;i++){
if( (x%i)==0){
check=false;
}
}
return check;
}
}

Prime Factorization in Java

I'm working on a prime factorization program in Java one that displays all prime factors of a number even if they are repeated. And I have this:
public static void factors(int a)
{
int c=1;
for(int i = 1; i <= a;i++)
{
if(a%i == 0)
{
for(int k = 2; k < i; k++)
{
if(i%k == 0)
{
c = 1;
break;
}
else
{
c = 0;
}
}
if(c == 0 || i == 2)
{
System.out.print(i+ ", ");
}
}
}
}
I need to account for repeated factors (as in 2, 2, 2 for 8). How could I do that without completely restructuring?
I think you should start over, and build an algorithm from this simple description:
Prepare a List<Integer> of prime numbers that are less than or equal to 2^16
Run through this list from low to high, trying each prime in turn as a candidate divisor
Every time you run into a working divisor, continually divide it out until you can no longer divide the number by it; then continue to the next prime
Once you reach the end of the list of primes, your remaining number should be printed as well, unless it is equal to 1.
Finding a list of primes is a fun problem in itself. Dijkstra wrote a fascinating chapter on it back in 1972. This article has a C++ implementation and a very nice discussion.
You can have another collection that maintains the factors and their count and can finally account for repeated factors. A map with counts would be my choice.
(1) if(c == 0 || i == 2) is wrong, it will print 2 for a == 5 as well.
(2) In order to do what you are asking without changing the code (*) - you should count how many times each prime factor is diviseable by the number. It can be done by simply adding a new loop before your print statement [pseudo code]:
boolean b = true;
int k = 1;
while (b) {
if (a % (int) Math.pow(i, k+1) == 0) k++;
else b = false;
}
at the end of this loop, k denotes how many times i is a prime factor of a.
(*) Note: Though this approach should work, I'd still go with #KerrekSB suggestion to a redesign.

Java - Can't make ProjectEuler 3 Work for a very big number (600851475143)

Resolution:
It turns out there is (probably) "nothing wrong" with the code itself; it is just inefficient. If my math is correct, If I leave it running it will be done by Friday, October 14, 2011. I'll let you know!
Warning: this may contain spoilers if you are trying to solve Project Euler #3.
The problem says this:
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
Here's my attempt to solve it. I'm just starting with Java and programming in general, and I know this isn't the nicest or most efficient solution.
import java.util.ArrayList;
public class Improved {
public static void main(String[] args) {
long number = 600851475143L;
// long number = 13195L;
long check = number - 1;
boolean prime = true;
ArrayList<Number> allPrimes = new ArrayList<Number>();
do {
for (long i = check - 1; i > 2; i--) {
if (check % i == 0) {
prime = false;
}
}
if (prime == true && number % check == 0) {
allPrimes.add(check);
}
prime = true;
check--;
} while (check > 2);
System.out.println(allPrimes);
}
}
When number is set to 13195, the program works just fine, producing the result [29, 13, 7, 5] as it should.
Why doesn't this work for larger values of number?
Closely related (but not dupe): "Integer number too large" error message for 600851475143
The code is very slow; it is probably correct but will run for an unacceptably large amount of time (about n^2/2 iterations of the innermost loop for an input n). Try computing the factors from smallest to largest, and divide out each factor as you find it, such as:
for (i = 2; i*i <= n; ++i) {
if (n % i == 0) {
allPrimes.add(i);
while (n % i == 0) n /= i;
}
}
if (n != 1) allPrimes.add(n);
Note that this code will only add prime factors, even without an explicit check for primality.
Almost all the Project Euler problems can be solved using a signed datatype with 64 bits (with the exception of problems that purposefully try to go big like problem 13).
If your going to be working with primes (hey, its project Euler, your going to be working with primes) get a headstart and implement the Sieve of Eratosthenes, Sieve of Atkin, or
Sieve of Sundaram.
One mathematical trick used across many problems is short circuiting finding factors by working to the square root of the target. Anything greater than the square corresponds to a factor less than the square.
You could also speed this up by only checking from 2 to the square root of the target number. Each factor comes in a pair, one above the square root and one below, so when you find one factor you also find it's pair. In the case of the prime test, once you find any factor you can break out of the loop.
Another optimization could be to find the factors before checking that they are prime.
And for very large numbers, it really is faster to experiment with a sieve rather than brute forcing it, especially if you are testing a lot of numbers for primes. Just be careful you're not doing something algorithmically inefficient to implement the sieve (for example, adding or removing primes from lists will cost you an extra O(n)).
Another approach (there is no need to store all primes):
private static boolean isOddPrime(long x) {
/* because x is odd, the even factors can be skipped */
for ( int i = 3 ; i*i <= x ; i+=2 ) {
if ( x % i == 0 ) {
return false;
}
}
return true;
}
public static void main(String[] args) {
long nr = 600851475143L;
long max = 1;
for ( long i = 3; i <= nr ; i+=2 ) {
if ( nr % i == 0 ) {
nr/=i;
if ( isOddPrime(i) ){
max = i;
}
}
}
System.out.println(max);
}
It takes less than 1 ms.

Categories