Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clean up my code

Being new to this I really am trying to learn how to keep code as simple as possible, whilst doing the job it's supposed to.

The question I have done is from Project Euler, it says

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

Find the sum of all the even-valued terms in the sequence which do not exceed four million.

Here is my code below. I was wondering what the best way of simplifying this would be, for a start removing all of the .get(list.length()-1 )..... stuff would be a good start if possible but I don't really know how to?

Thanks

public long fibb()
{
    ArrayList<Integer> list = new ArrayList<Integer>();


    list.add(1);
    list.add(2);

    while((list.get(list.size() - 1) +  (list.get(list.size() - 2)) < 4000000)){  
        list.add((list.get(list.size()-1)) + (list.get(list.size() - 2)));
    }     

    long value = 0;
    for(int i = 0; i < list.size(); i++){
        if(list.get(i) % 2 == 0){
            value += list.get(i);
        }    
    }
    return value;
}
like image 300
simion Avatar asked Jun 14 '10 13:06

simion


Video Answer


1 Answers

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;
}
like image 170
John Kugelman Avatar answered Nov 05 '22 23:11

John Kugelman