The other responders have all given great answers. I want to show you how refactoring works in action, not just for this specific problem knowing things about Fibonacci numbers, but as an iterative process that carefully whittles down code to the bare minimum. Refactoring lets us start with working but complicated code and steadily pare it down one step at a time. Let me show you all of the in between steps you could make while working your way towards the final solution.
Note: I've changed your initial starting values to 1 and 1 instead of 1 and 2. Strictly speaking, the Fibonacci sequence begins with two 1's, as in 1, 1, 2, 3, 5...
Step 1 - Reverse the list
For starters, to get rid of the repetitive list.size() - x
expressions you could add the numbers in reverse order. Then finding the two most recent numbers is simpler.
public long fibb()
{
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(1);
while (list.get(0) + list.get(1) < 4000000) {
// Use list.add(0, ...) to add entries to the *front*.
list.add(0, list.get(0) + list.get(1));
}
long value = 0;
for (int i = 0; i < list.size(); i++) {
if (list.get(i) % 2 == 0) {
value += list.get(i);
}
}
return value;
}
Step 2 - Switch to a linked list
Let's switch the ArrayList
to a LinkedList
. Inserting at the beginning of an array is inefficient, whereas its a quick operation on a linked list.
Along those lines, we'll need to get rid of the get()
calls in the second loop. Looking up entries by index is slow using linked lists. To do this I've changed the second loop to use for (variable: container)
syntax.
public long fibb()
{
// Changed to use a linked list for faster insertions.
List<Integer> list = new LinkedList<Integer>();
list.add(1);
list.add(1);
// Using get() is normally a bad idea on linked lists, but we can get away
// with get(0) and get(1) since those indexes are small.
while (list.get(0) + list.get(1) < 4000000) {
list.add(0, list.get(0) + list.get(1));
}
long value = 0;
// Altered loop to avoid expensive get(i) calls.
for (Integer n: list) {
if (n % 2 == 0) {
value += n;
}
}
return value;
}
Step 3 - Combine the loops
The next optimization is to combine the two loops. Instead of generating all of the numbers first and then checking for even ones later, you could check for even numbers as you generate them.
public long fibb()
{
List<Integer> list = new LinkedList<Integer>();
long value = 0;
list.add(1);
list.add(1);
while (list.get(0) + list.get(1) < 4000000) {
int next = list.get(0) + list.get(1);
list.add(0, next);
if (next % 2 == 0) {
value += next;
}
}
return value;
}
Step 4 - Eliminate the list
Now you might notice that you never refer to numbers beyond index 1. Numbers at positions 2 and beyond are never used again. This hints that you don't even need to keep a list of all the numbers any more. Since you are checking for the even numbers as they are generated, you can now throw away all but the two most recent numbers.
Also, as a minor detail, let's rename value
to total
.
public long fibb()
{
int a = 1, b = 1;
long total = 0;
while (a + b < 4000000) {
// Calculate the next number.
int c = a + b;
// Check if it's even.
if (c % 2 == 0) {
total += c;
}
// Shift the values.
a = b;
b = c;
}
return total;
}