Как очистить мой код

Будучи в новинку для этого, которое я действительно пытаюсь изучить, как сохранить код максимально простым при выполнении задания, это, как предполагается.

Вопрос, который я сделал, от Euler Проекта, говорит он

Каждый новый термин в последовательности Fibonacci сгенерирован путем добавления предыдущих двух сроков. Путем запуска с 1 и 2, первые 10 сроков будут:

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

Найдите сумму всех условий с ровным знаком в последовательности, которые не превышают четыре миллиона.

Вот мой код ниже. Я задавался вопросом, каково лучший способ упростить это будет, для запуска, удаляющего все .get (list.length ()-1)....., наполняют, было бы хорошее начало, если возможный, но я действительно не знаю как к?

Спасибо

public long fibb()
{
    ArrayList list = new ArrayList();


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

7
задан Francisco Puga 12 March 2018 в 14:38
поделиться

10 ответов

Все другие респонденты дали отличные ответы. Я хочу показать вам, как работает рефакторинг, а не только для этой конкретной проблемы, зная кое-что о Фибоначчи. чисел, но как итеративный процесс, который тщательно сокращает код до минимума. Рефакторинг позволяет нам начать с рабочего, но сложного кода и постепенно сокращать его по одному шагу за раз. Позвольте мне показать вам все промежуточные шаги, которые вы могли бы сделать пока вы работаете над окончательным решением.

Примечание: я изменил ваши начальные начальные значения на 1 и 1 вместо 1 и 2. Строго говоря, последовательность Фибоначчи начинается с двух единиц, как в случаях 1, 1, 2 , 3, 5 ...

Шаг 1 - Переверните список

Для начала, чтобы избавиться от повторяющихся выражений list.size () - x , вы можете добавить числа в ] обратный порядок. Тогда поиск двух последних номеров станет проще.

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

Шаг 2 - Переключитесь на связанный список

Давайте переключим t он ArrayList в LinkedList .Вставка в начало массива неэффективна, тогда как для связанного списка это быстрая операция.

Таким образом, нам нужно избавиться от вызовов get () во втором цикле. Поиск записей по индексу при использовании связанных списков выполняется медленно. Для этого я изменил второй цикл, чтобы использовать синтаксис для (переменная: контейнер) .

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

Шаг 3 - Объединение циклов

Следующая оптимизация заключается в объединении двух циклов. Вместо того, чтобы сначала генерировать все числа, а потом проверять четные числа, вы можете проверять четные числа по мере их создания.

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

Шаг 4 - Удалите список

Теперь вы можете заметить, что никогда не ссылаетесь на числа после индекса 1. Числа в позициях 2 и далее больше никогда не используются. Это намекает на то, что вам даже не нужно больше вести список всех чисел. Поскольку вы проверяете четные числа по мере их генерации, теперь вы можете выбросить все числа, кроме двух последних.

Также, в качестве незначительной детали, давайте переименуем значение в 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;
}
33
ответ дан 6 December 2019 в 04:49
поделиться

Вы можете полностью избавиться от списка. Просто сохраните два последних числа Фибоначчи в двух переменных и вычислите следующее в цикле (аналогично вашему первому циклу).

Затем сохраните промежуточную сумму всех чисел, которые соответствуют условию (т. Е. Четные и меньше миллиона) в том же цикле .

3
ответ дан 6 December 2019 в 04:49
поделиться

В Python все выглядит красивее!

def fibb():
    ret = 2 # to take into account the first 2
    fib = [1, 2]

    while True:
        latest = fib[-1] + fib[-2]
        if latest >= 4000000: return ret

        fib.append(latest)
        if latest % 2 == 0: ret += latest

Примечание: это было больше упражнение по программированию, чем что-либо еще. Я понимаю, что переходить на Python, вероятно, нецелесообразно.

Отредактируйте, вот более эффективный с точки зрения памяти подход без использования списков:

def fibb():
    ret = 0
    f0, f1 = (1, 1)

    while True:
        f0, f1 = (f1, f0 + f1)
        if f1 >= 4000000: return ret
        if f1 % 2 == 0: ret += f1
1
ответ дан 6 December 2019 в 04:49
поделиться

Вам не нужен список, вам нужны только два последних пункта. Вот какой-то псевдокод, перевод на ваш язык оставляю вам.

f0=1 #pre-last number
f1=1 #last number
while(...) {
    t = f0 + f1
    if (t%2 == 0) # replaces your second loop 
         sum += t 
    f0 = f1
    f1 = t
}

Затем вы можете заметить, что числа всегда идут в последовательности:

odd, odd, even, odd, odd, even [...]

и оптимизируйте дальше, если вам нужно

10
ответ дан 6 December 2019 в 04:49
поделиться

Прежде всего, прежде чем пытаться переписать какой-либо код, полезно провести модульный тест. Даже самое очевидное изменение может сломать нечто неожиданное.

Во-вторых, важно быть очень осторожным. Вы часто увидите в коде, что X-вызовы одних и тех же функций могут быть заменены одним вызовом, присвоением переменной и использованием этой переменной.

Однако при этом нужно быть осторожным. Например, в вашем текущем коде вы не можете действительно заменить list.size (), потому что размер изменяется все время между вызовами в одной и той же итерации.

Что в основном затрудняет понимание вашего кода, так это то, что вы ссылаетесь на индексы списков, когда обновляете списки. Вам нужно сохранить индексы в переменных, разделить на несколько строк и, возможно, добавить некоторые комментарии, чтобы сделать его более читабельным.

3
ответ дан 6 December 2019 в 04:49
поделиться

Ну, во-первых, член последовательности Фибоначчи порождается двумя предыдущими значениями перед ним. Почему бы просто не хранить два числа в буфере и не проверить, является ли число четным. Если оно четное, добавьте его к своей сумме и остановитесь, когда достигнете четырех миллионов.

код:

public int fibEvenSum(int a, int b) {

int sum = 0;
int c = a + b;

while (c <= 4000000) {

if (c % 2 == 0) {
sum += c;
}

//Shift the sequence by 1
a = b;
b = c;

c = a + b;

} //repeat

return sum;
}
0
ответ дан 6 December 2019 в 04:49
поделиться

Вот мой совет (я не собираюсь давать ответ, так как всегда лучше делать выводы самостоятельно)

Постарайтесь подумать, почему вам нужно сохранять значения последовательности? Ваша программа будет хранить 4000000 целых чисел в большом массиве, действительно ли в этом есть необходимость? Вы можете просто использовать 2 элемента и пролистывать их (используя расчет Фибоначчи), добавляя четные числа к переменной Total.

0
ответ дан 6 December 2019 в 04:49
поделиться

Вы бы сильно упростили, если бы не сохранили все значения в списке. Вы можете проверить, находятся ли они даже «на ходу»

, например так:

int old, current;
old = 1;
current = 1;

long value = 0;

while(current < 4000000) {
 if(current % 2  == 0) value += current;

 int tmp = old + current;
 old = current;
 current = tmp;
}

return value;
0
ответ дан 6 December 2019 в 04:49
поделиться

Я бы отбросил список. Здесь вы выполняете две итерации, когда все, что вам действительно нужно, это перебирать последовательность и сохранять переменную подсчета по мере продвижения.

  • Итак, перебираем последовательность Фибоначчи, используя какой-то цикл for / while.
  • Добавьте его к переменной счета, если он четный.

Чтобы выполнить простую итерацию по последовательности, вам нужно записать только два самых последних значения ( f (n) и f (n-1) ) и (в зависимости от на вашем языке) временная переменная, когда вы обновляете эти значения. Обычно что-то вроде:

temp = current;
current = current + previous; // temp holds old value of current so the next line works.
previous = temp;

Простой подход - это базовый цикл for. Или вы можете создать свой собственный класс, реализующий интерфейс Iterator , если хотите, чтобы он был полностью объектно-ориентированным. Тогда ваш код мог бы быть таким простым, как:

Fib fib = new Fib();
for(long n : fib) {
    if(n % 2 == 0) t += n;
}

Это напомнило мне, что однажды я должен снова вернуться к Эйлеру.

1
ответ дан 6 December 2019 в 04:49
поделиться

Как решение @John Kugelman, но более эффективное. Этот цикл будет повторяться только 1/3 времени, и каждый цикл будет короче с меньшим количеством ветвлений и без необходимости проверки на четность. Здесь используется тот факт, что каждое третье значение fib является четным, и просто вычисляются значения между ними, чтобы сбросить значения a и b.

public static long fibb() {
    int a = 1, b = 1;
    long total = 0;
    while (true) {
        int c = a + b;
        if (c >= 4000000) return total;
        total += c;
        a = b + c;
        b = c + a;
    }
}
0
ответ дан 6 December 2019 в 04:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: