Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trial
Diego Murray
2,515 PointsHelp Debugging Euler Problem 23 - JAVA
PROBLEM: A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.
A number n is called deficient if the sum of its proper divisors is less than n and it is called abundant if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
**** I am not getting the correct answer. Any pointers on what I'm doing wrong would be great. Additionally, ways restructure my code to make it faster.
MY CODE:
import java.util.ArrayList;
public class Problem023 {
public static final int MAX = 28123;
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
int sum = 0;
int temp;
ArrayList<Integer> abundantNumbers = new ArrayList<Integer>();
ArrayList<Integer> abundantSums = new ArrayList<Integer>();
for (int i = 12; i < (MAX / 2) + 1; i++) {
if(isAbundant(i)) {
abundantNumbers.add(i);
}
}
int listSize = abundantNumbers.size();
for(int i = 0; i < listSize; i++) {
for (int j = i; j < listSize; j++) {
temp = abundantNumbers.get(i) + abundantNumbers.get(j);
abundantSums.add(temp);
}
}
for (int i = 1; i < listSize; i++) {
if (abundantSums.contains(i) == false) {
sum += i;
}
}
System.out.println("Answer: " + sum);
long endTime = System.currentTimeMillis();
System.out.println("Time: " + (endTime - startTime)/1000 + " seconds");
}
public static boolean isAbundant(int n) {
int sum = 0;
for (int i = 1; i < n; i++) {
if (n % i == 0)
sum += i;
}
if(sum > n) {
return true;
} else {
return false;
}
}
}
Diego Murray
2,515 PointsShane Robinson My bad changed it. I am not getting the correct answer.
2 Answers
Jason Anello
Courses Plus Student 94,610 PointsHi Diego,
Your code looks pretty similar to the one in the stackoverflow link that Alexander gave.
You didn't make the same error as the one on stackoverflow but I noticed 2 things that needed correction.
When getting a list of abundant numbers, you were stopping at the halfway point and I wasn't sure why.
I think you'll be missing certain sums if you do that. For example, 15000 is an abundant number and adding to 12 gives the sum 15012. You would be missing that sum from your list unless some lower abundant numbers happened to give that same sum.
The other issue I found was when you were calculating the final sum.
for (int i = 1; i < listSize; i++) {
if (abundantSums.contains(i) == false) {
sum += i;
}
}
You have to go through all the numbers. You're only doing as many as you have abundant numbers. If you had 10,000 abundant numbers then this means you're only checking the first 10,000 positive integers.
Here's the 2 fixes with your original lines commented out.
// around line 16
// for (int i = 12; i < (MAX / 2) + 1; i++) {
for (int i = 12; i <= MAX; i++) {
// around line 33
// for (int i = 1; i < listSize; i++) {
for (int i = 1; i <= MAX; i++) {
After those 2 fixes I was able to get the result 4179871 which matches up with the reported correct answer on that stackoverflow link. I'll assume that's correct unless you know otherwise.
You mentioned making the program run faster which I think you may need to do. It's running pretty slow for me. I haven't solved any of the project euler problems but are they time restricted?
Let me know what kind of time you're getting.
I have some ideas for optimization which I need to try out and then I can post as a follow up comment.
Diego Murray
2,515 PointsJason Anello Thanks so much for the help. Generally, I've heard for project euler problems people try to keep it under a minute. With your correction I got the correct answer but it took 142 seconds. Any recomendations?
Jason Anello
Courses Plus Student 94,610 PointsI think once your solution is accepted you gain access to a private forum where different solutions and algorithms can be discussed. I recommend that you read as much of that as you can because you'll learn far more than whatever I can tell you here.
I'm on a slower computer than you. It was 400 seconds for me but I was able to get it down to about 1.5 seconds. It might end up being less than a second for you.
One thing I forgot to mention before is that you should change your timing calculation to a floating point calculation so you don't lose the milliseconds. It doesn't matter much right now but when your times are faster it will be good to have.
System.out.println("Time: " + (endTime - startTime)/1000.0 + " seconds");
I don't know how much you know so hopefully I'm not explaining too much that you already know.
I ended up doing the optimizations in reverse order of effectiveness. The last one I did had the biggest improvement.
I started with isAbundant but its the final for loop checking the sums that takes up the majority of the time.
The contains method on an ArrayList operates in linear time. Meaning you have to look at every number in the array to know whether each number is in there. So you're checking 28,000 numbers against however many sums there are.
I don't know if you are familiar with big O notation and the running time of algorithms but here's a graph on wikipedia: https://en.wikipedia.org/wiki/Big_O_notation#/media/File:Comparison_computational_complexity.svg
The green line represents linear time and the purple or pink line (labeled 1) at the bottom is constant time. You can see how much more quickly the linear line grows as compared to both the logarithmic and constant time. Either of those would be preferable over linear if given the choice.
By switching over to a HashSet you can search in constant time: https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html
This class offers constant time performance for the basic operations (add, remove, contains and size), ...
Plus the added benefit of no duplicate sums.
HashSet<Integer> abundantSums = new HashSet<Integer>();
That should take out a huge chunk of the time.
Another thing you can do with the sums is not add them if they are greater than the MAX value. Although this likely won't have any affect since a HashMap is used.
if (temp <= MAX) {
abundantSums.add(temp);
}
The only other thing that I thought of was the is_abundant method. You don't have to check for divisors all the way up to the number you're checking. You only have to go up to the square root of the number. This really cuts down on the number of mod operations you have to perform.
Divisors come in pairs, like 2 and 12 are divisor pairs for 24. So if you know that 2 is a divisor then you can get the 12 by doing 24/2 You don't actually have to loop all the way up to the 12 and do a mod operation to see that it goes into 24 evenly.
public static boolean isAbundant(int n) {
int sum = 1;
int upper = (int) Math.sqrt(n);
for (int i = 2; i <= upper; i++) {
if (n % i == 0) {
sum += i;
if (i*i != n) {
sum += n/i;
}
}
}
if(sum > n) {
return true;
} else {
return false;
}
}
Let me know if you have any questions.
Also, let me know what your time is, I'm curious.
Alexander Nikiforov
Java Web Development Techdegree Graduate 22,175 PointsHave you tried googling ?
It seems that this is known problem...
like here:
Shane Robinson
7,324 PointsShane Robinson
7,324 PointsI think you mean problem 23, because that is not the same as problem 32. :p Also, what are you looking for from us? Is your code not working, getting an error? Or is it working and you just want to know if there are better approaches to solving the problem?