Project Euler 12 - Optimization [closed] - java

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 9 years ago.
Improve this question
I am working on Project Euler Problem 12. Could anyone provide any tips on how to improve my code so it executes in my life time?
public class HighlyDivisibleTriangularNumber {
public static void main(String[] args) {
int divisors = 0;
int count = 1;
while(divisors <= 501) {
long triNum = triangularNumber(count);
divisors = getFactors(triNum);
System.out.println(triNum+"_"+divisors);
count++;
}
}
private static int getFactors(long triNum) {
int divisors = 0;
while(triNum > 1) {
triNum = triNum / 2;
divisors++;
}
return divisors;
}
private static long triangularNumber(int i) {
long total = 0;
for(int k = 1; k <= i; k++) {
total += k;
}
return total;
}
}

1) triangular numbers
The first (and probably most important) optimization you can do is in how you compute the triangular numbers.
You can observe that the nth triangular number (let's call it t(n) ) is equal to n + t(n-1).
So each time you compute a triangular number, you can just take the triangular number before it and add n. This would lead to the naive recursive function :
private static long triangularNumber(int i) {
if(i == 1) return 1;
else return i+triangularNumber(i-1);
}
But this won't improve the performance much... to resolve this, I suggest you do some research on memoization and adapt the function I gave you (I won't give you the answer, this is an excellent exercise)
Now, on a regular computer you should have the answer to the problem in a reasonable time. But it can be improved a little better
2) counting divisors
Your function for counting divisors is wrong. What you should do is try to divide your number by successive natural numbers and see if the result is an natural integer.
private static int getFactors(long triNum) {
int divisors = 0;
for(int i = 1; i <= triNum; ++i) {
if(triNum%i == 0) // triNum is a multiple of 1 <=> i is a divisor of triNum
divisors++;
}
return divisors;
}
You can even improve this by counting only to the square root of trinum and adding two divisors each time. But there's a trick if you do this, I'll let you figure it out if you decide to try this.

Why do do recompute the triNum each time? Just add the difference each time (basically your count).
public static void main(String[] args) {
int divisors = 0;
int count = 1;
long truNum = 0;
while(divisors <= 501) {
triNum += count;
divisors = getFactors(triNum);
System.out.println(triNum+"_"+divisors);
count++;
}
}
Furthermore, your approach to count the factors is completely off. You are just searching for the first power of two to be greater than the given number. Read up on (prime)-factorization. Note that you need to account for the combinations of (prime) factors, too.
Example: 12
12 = 2 * 2 * 3
But the divisors of 12 are
1, 2, 3, 4 (= 2*2), 6 (= 2*3), 12
So in total there are 6 divisors of 12 and not 3 as the mere prime factorization may lead you to believe.

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;
}

Java- Why isn't my code producing the correct results? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I was solving problem 4 of ProjectEuler:
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.
I couldn't get it right. This is my code:
public static long reverseNumber(long number){
long reversed = 0;
while(number != 0) {
long digit = number % 10;
reversed = reversed * 10 + digit;
number /= 10;
}
return reversed;
}
long sum,finalSum=1,revSum;
for (int i=100;i<1000;i++){
for (int j=100;j<1000;j++){
sum=i*j;
revSum=reverseNumber(sum);
if (sum==revSum){
finalSum=sum;
}
}
}
System.out.println(finalSum);
This is some code I found online and it worked perfectly:
int maxPalin = -1;
for (int i = 100; i < 1000; i++) {
for (int j = 100; j < 1000; j++) {
int prod = i * j;
if (prod > maxPalin){
if (reverseNumber(prod)>maxPalin) {
maxPalin = prod;
}
}
}
}
System.out.println(Integer.toString(maxPalin));
But what's wrong with mine?
The goal is to find the max palindrome which is a product of two numbers between 100 and 999. The issue with your code is that you assume that last palindrome found would be the largest. This assumption is wrong.
For each palindrome you find, you should check if it's larger than the last palindrome you found before choosing it as a candidate for being the max palindrome.
BTW, the second snippet you posted is also incorrect, since it doesn't actually check that the current product is a palindrome (i.e. that prod==reverseNumber(prod)).
A correct implementation would be:
public static void maxp () {
int maxPalin = -1;
for (int i = 100; i < 1000; i++) {
for (int j = 100; j < 1000; j++) {
int prod = i * j;
if (reverseNumber(prod) == prod && prod > maxPalin) {
maxPalin = prod;
}
}
}
System.out.println(maxPalin);
}
Your code returns a palindrome that isn't the max palindrome (the product of 995 and 583) :
580085
The second snippet returns a number that isn't a palindrome at all:
980099
The correct answer is the product of 913 and 993:
906609
Your implementation didn't find it because you overwrote it with a palindrome found in a later iteration, that was a product of a higher i with a smaller j.

Java program taking forever to run with large numbers

I am writing a Java program that calculates the largest prime factor of a large number. But I have an issue with the program's complexity, I don't know what has caused the program to run forever for large numbers, it works fine with small numbers.
I have proceeded as follow :
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class Largest_prime_factor {
public static void main(String[] args)
{
//ArrayList primesArray = new ArrayList();
ArrayList factorArray = new ArrayList();
long largest = 1;
long number = 600851475143L ;
long i, j, k;
//the array list factorArray will have all factors of number
for (i = 2; i < number; i++)
{
if( number % i == 0)
{
factorArray.add(i);
}
}
Here, the Array List will have all the factors of the number.
So I'll need to get only the prime ones, for that, I used a method that checks if a number is prime or not, if it's not a prime number, I remove it from the list using the following method :
java.util.ArrayList.remove()
So the next part of the code is as follow :
for (i = 2; i < number; i++)
{
if (!isPrime(i))
{
factorArray.remove(i);
System.out.println(factorArray);
}
}
System.out.println(Collections.max(factorArray));
}
The last line prints the largest number of factorArray, which is what I am looking for.
public static boolean isPrime(long n)
{
if(n > 2 && (n & 1) == 0)
return false;
for(int i = 3; i * i <= n; i += 2)
if (n % i == 0)
return false;
return true;
}
}
The function above is what I used to determine if the number is a prime or not before removing it from the list.
This program works perfectly for small numbers, but it takes forever to give an output for large numbers, although the last function is pretty fast.
At first, I used to check if a number is prime or not inside of the first loop, but it was even slower.
You are looping over 600851475143 numbers.
long number = 600851475143L ;
for (i = 2; i < number; i++)
Even if we assume that each iteration takes very very small time (as small as 1 microsecond), it'll still take days before the loop finishes.
You need to optimise your prime-finding logic in order for this program to run faster.
One way to reduce the iterations to reasonable number is to loop until square root of number.
for (i = 2; i < Math.sqrt(number); i++)
or
for (i = 2; i*i < number; i++)
The calculation of the prime factors of 600851475143L should take less than a milli-second (with a not totally inefficient algorithm). The main parts your code is currently missing:
The border should be sqrt(number) and not number.
The current value should be checked in a while-loop (to prevent that non-prime-factors are added to the list, reduces range to check).
The max. value should be decreased (as well as the border) to number/factor after finding a factor.
Further improvements are possible, e.g. to iterate only over non-even numbers (or only iterate over numbers that are neither a multiple of 2 and 3) etc.
An example implementation for the same question on codereview (link):
public static long largestPrimeFactor(
final long input) {
////
if (input < 2)
throw new IllegalArgumentException();
long n = input;
long last = 0;
for (; (n & 1) == 0; n >>= 1)
last = 2;
for (; n % 3 == 0; n /= 3)
last = 3;
for (long v = 5, add = 2, border = (long) Math.sqrt(n); v <= border; v += add, add ^= 6)
while (n % v == 0)
border = (long) Math.sqrt(n /= last = v);
return n == 1 ? last : n;
}
for (i = 2; i < number; i++)
{
if( number % i == 0)
{
factorArray.add(i);
}
}
For an large input size, you will be visiting up to the value of the number. Same for the loop of removing factors.
long number = 600851475143L ;
this is a huge number, and you're looping through this twice. Try putting in a count for every 10,000 or 100,000 (if i%10000 print(i)) and you'll get an idea of how fast it's moving.
One of the possible solutions is to only test if the the prime numbers smaller than the large number divide it.
So I checked
for (i=2; i < number; i++)
{
if(isPrime(i))
{
if( number % i == 0)
{
factorArray.add(i);
}
}
}
So here I'll only be dividing by prime numbers instead of dividing by all numbers smaller than 600851475143.
But this is still not fast, a complete modification of the algorithm is necessary to obtain an optimal one.
#Balkrishna Rawool suggestion is the right way to go. For that I would suggest to change the iteration like this: for (i = 3; i < Math.sqrt(number); i+=2) and handle the 2 manually. That will decrease your looping because none of the even numbers except 2 are prime.

Shortest distance between factors without using array, data structures or any api in java? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I was asked a question to find a shortest distance between factors of a number without using arrays, any data structures or string in Java. For example if 13013 is passed to a method it should return 2, since the factors of 13013 are 1*7*11*13*13 and the shortest distance between two different number is 13-11=2, one more example if 10 is passed it should return 1, since factors of 10 are 1*2*5 and the shortest distance is (2-1) which is 1. What i found was if even number is passed it should always return 1, since 2, and 1 is always involved in the factor and (2-1)=1, if prime is passed it will be (number-1), since prime is only divisible by itself and 1, and if odd is passed mostly it is 2 Since (3-1)=2, but I am not being able to find for all numbers.
This is what I have till this point, thank you all
static int findDistance(int numberPassed)
{
boolean isPrime=true;
int returnValue=0;
if(numberPassed % 2==0)
{
//If it is even there must be 2 and 1 as a factor therefore 2-1=1
returnValue=1;
}
else
{
for(int i=2;i<numberPassed;i++)
{
if(numberPassed % i==0)
{
//if number is divisible by any except by 1 and itself
//it is prime
isPrime=false;
break;
}
}
if(isPrime && numberPassed!=1)
{
//If it is prime then return value should be numberPassed-1
//since factor of prime are 1 and itself, so shortest distance
//numberPassed-1
returnValue=numberPassed-1;
}
else
{
//The problem is here if the number is not prime and is odd
//since shortest distance differes now, for value divisible by 3
//it should be 3-1=2, but there are many other cases
returnValue=2;
}
}
return returnValue;
}
static int findDistance(int a)
{
//variables needed for calculation
int temp=a;
int fact=1;
int distance=0;
int shortestDistance=a;
int lastFact=1;
while(temp>1)
{
//If value is divisible by fact variable
if(temp % fact==0)
{
//If last fact does not equal current fact
if(lastFact!=fact)
{
distance=fact-lastFact;
lastFact=fact;
}
//divide by fact
temp=temp/fact;
//reassign the fact to 1 so that it can start from beginning
fact=1;
//if distance is not zero and shortestDistance is bigger
if(shortestDistance>distance && distance !=0)
{
//now the shortest distance is the distance
shortestDistance=distance;
}
}
fact++;
}
return shortestDistance;
}
public static int minDistance(int num){
int fact1 = 1;
int minDist = num;
int fact2 = 0;
for(int i=2; i<num; i++){
if (num%i==0){
fact2 = i;
if((fact2-fact1)<=minDist){
minDist = fact2-fact1;
fact1 = fact2;
}
}
}
return (minDist);
}
public class Demo {
public static void main(String[] args) {
System.out.println("Result: " + minDistance(1001));
}
public static int minDistance(int n) {
int minDistance = n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (n % i == 0 && n % j == 0) {
if (i > j) {
int distance = i - j;
if (distance < minDistance) {
minDistance = distance;
}
}
}
}
}
return minDistance;
}
}
Here,two for-loops are used: Outer for-loop is used to take one value at a time and calculate distance between two value(Second value is obtained from Inner for-loop) and stored in distance variable. Since minimum distance between the factors of the number will always be less than itself, so the minDistance is initialized to the number itself. If the distance is less than minDistance value then minDistance is reset to new distance value.

Program taking forever to run [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
public class Problem_3 {
public static void main(String... args) {
long limit = 600851475143L;
long largestPrimeFactor = 0;
for (long number = 2; number < limit; number++) {
if (isPrime(number)) {
if ((limit % number == 0)){
largestPrimeFactor = number;
}
}
}
System.out.println(largestPrimeFactor);
}
public static boolean isPrime(long number) {
for (int i = 2; i < number; i++) {
if (number % i == 0) {
return false;
}
}
return true;
}
}
I'm sure, the above program is not in infinite loop. I tested with limit = 13195; and got desired result 29
I don't understand why my CPU is taking forever to run it.
EDIT:
Its my code for ProjectEuler.net problem number 3.
Your algorithm has a time complexity of O(n^2), making it not very effective for larger values of your limit.
So it's slow because you're using a poor algorithm.
Time complexity of your code is O(N^2). So it's not a good algorithm for large numbers. A possible suggestion would be as follows -
You are interested in largest prime factor then why not start from the largest value?
for (long number = limit-1; number > 1; number--) {
if (isPrime(number)) {
if ((limit % number == 0)){
largestPrimeFactor = number;
break;
}
}
}
Though time complexity remains the same this would definitely reduce your time than your present algorithm.
But, what exactly is time complexity..?? and how do u calculate it..?? and what is O(N^2) ?? please explain
for (long number = 2; number < limit; number++)
Above is your first for loop in which you will have n iterations. Inside this you call isPrime() function which again has a for loop in it for (int i = 2; i < number; i++) again having n iterations. So basically you have two for loops one inside the other which makes your time complexity n*n = n^2
This is the reason.
long limit = 600851475143L;
and an O(n^2) algorithm and n is this long number 600851475143L
you are passing each no to the isPrime(long number) method & again you have taken the for loop "for (int i = 2; i < number; i++) {" which is running for another (number-1) every times. means you are running this 600851475141*600851475141 times.
You could have an idea how much time it will take ...........may be you need a super comp for this.....
Happy coding
Because of the nesting of your loops (one loop up to limit in main and another loop called from within that loop, within isPrime) the time complexity of your algorithm is O(n^2).
This is a standard way to express how long an algorithm takes to solve a different problem for different sized inputs.
In your case, the size is the limit (600851475143L) so to solve the problem up to this will take "an order of 600851475143 ^ 2", compared to, "10 ^ 2 (i.e. 100)", if you had a limit of 10 (for example)
So if the version with a limit of 10 took 100 milliseconds (10 squared), your 600851475143 version would take something like 361022495181519000000000 milliseconds (i.e. 600851475143L squared, apologies if I got some zeroes wrong there)
Other examples of time complexity would be O(1), or constant time, where the algorithm takes the same time regardless of the size, or O(n), or linear time, where the time taken is directly proportional to the size
As all said your code is not very time efficient but looking at the code i can guess that you are working to find the biggest prime factor to do that you can actually try Sieve of Eratosthenes to generate all prime then find the factor from back.
import java.util.LinkedList;
class Sieve{
public static LinkedList<Long> sieve(long n){
if(n < 2) return new LinkedList<Long>();
LinkedList<Long> primes = new LinkedList<Long>();
LinkedList<Long> nums = new LinkedList<Long>();
for(long i = 2;i <= n;i++){ //unoptimized
nums.add(i);
}
while(nums.size() > 0){
long nextPrime = nums.remove();
for(long i = nextPrime * nextPrime;i <= n;i += nextPrime){
nums.removeFirstOccurrence(i);
}
primes.add(nextPrime);
}
return primes;
}
}
the above code returns the linked list of prime numbers use this start from last and get your largest factor.
NOTE:increase the heap size of jvm before running for the input of 600851475143L

Categories