Java 8 Лямбда-выражения для решения фибоначчи (нерекурсивный способ)

Я начинаю использовать функцию выражения Lambda в Java 8. Лямбда-выражения довольно полезны при решении таких программ, как проверка номера, факториал и т.д.

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

boolean checkPrime=n>1 && LongStream.range(2, (long) Math.sqrt(n)).parallel().noneMatch(e->(n)%e==0);

В приведенном выше коде в методе noneMatch мы оцениваем текущее значение (e) в диапазоне. Но для задачи Фибоначчи нам нужны предыдущие два значения.

Как мы можем это сделать?

Ответ 1

Простейшим решением является использование потока Pair s:

Stream.iterate(new long[]{ 1, 1 }, p->new long[]{ p[1], p[0]+p[1] })
      .limit(92).forEach(p->System.out.println(p[0]));

Из-за отсутствия стандартного типа пары он использует двухэлементный массив. Кроме того, я использую .limit(92), поскольку мы не можем оценить большее количество элементов, используя значения long. Но его легко адаптировать к BigInteger:

Stream.iterate(new BigInteger[]{ BigInteger.ONE, BigInteger.ONE },
               p->new BigInteger[]{ p[1], p[0].add(p[1]) })
      .forEach(p->System.out.println(p[0]));

Это будет работать до тех пор, пока у вас не будет достаточно памяти для представления следующего значения.

BTW, чтобы получить n-й элемент из потока:

Stream.iterate(new long[]{1, 1}, p -> new long[]{p[1], p[0] + p[1]})
    .limit(91).skip(90).findFirst().get()[1];

Ответ 2

Чтобы получить N-й элемент Фибоначчи (используя сокращение):

Stream.iterate(new long[] {1, 1}, f -> new long[] {f[1], f[0] + f[1]})
    .limit(n)
    .reduce((a, b) -> b)
    .get()[0];

Вот что происходит:

  • Stream.iterate() - создает пары чисел, каждая из которых содержит 2 последовательных элемента Фибоначчи. Мы должны использовать пары, потому что мы можем получить доступ только к последнему элементу с помощью "итерации", а не к 2 или более предыдущим элементам, поэтому для создания новой пары мы получаем последнюю пару, которая уже содержит 2 предыдущих элемента Фибоначчи, и производим следующая пара. И чтобы получить K-й элемент Фибоначчи, нам просто нужно получить левое значение из K-й пары.

  • .limit(n) - сохранить первые N пар и исключить остальные.

  • .reduce((a, b) → b) - получить последнюю пару из потока из N пар предыдущего шага.

  • .get() [0] - извлечь элемент Фибоначчи из пары (левое значение пары)

Ответ 3

решение фибоначчи (нерекурсивный способ)

Это не произойдет с вашим подходом

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

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

Итак, чтобы ответить на ваш вопрос в конце, а именно, как я могу использовать Лямбда-выражения на двух предыдущих элементах?: иметь список кортежей, каждый из которых содержит два последовательных числа, и перебирать их, добавляя новый набор для каждого шага.

Ответ 4

Если вам нужна нерекурсивная реализация, чтобы найти n-е число последовательности Фибоначчи, вы можете использовать формулу:

Un = ( (1+sqrt(5))^n - (1-sqrt(5))^n ) / (2^n * sqrt(5))

Binet's Fibonacci Number Formula

long fibonacci(int n) {
    return (long) ((Math.pow(1 + Math.sqrt(5), n) - Math.pow(1 - Math.sqrt(5), n)) /
        (Math.pow(2, n) * Math.sqrt(5)));
}

Ответ 5

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

public class FibonacciGenerator {

        private long prev=0; 

        public void printSequence(int elements) {

            LongStream.iterate(1, n -> {n+=prev; prev=n-prev; return n;}).           
            limit(elements).forEach(System.out::println);
        }
    }

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

Обратите внимание, что вы не можете использовать локальную переменную (объявленную в методе или переданную методу) вместо поля, поскольку такие переменные должны быть окончательными, чтобы использовать их в лямбдах. Для нашей цели нам понадобилась изменяемая переменная для хранения различных значений во время итерации.