Java 8个求解斐波那契的Lambda表达式(非递归方式)

Java 8 Lambda expressions for solving fibonacci (non recursive way)

我是初学者Java 8. Lambda 表达式在解决质数校验、阶乘等程序时非常有用

然而,它们可以有效地用于解决当前值取决于前两个值之和的斐波那契等问题。我已经很好地使用 Lambda 表达式有效地解决了质数检查问题。下面给出了相同的代码。

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

在上述 noneMatch 方法的代码中,我们使用范围内的当前值 (e) 进行评估。但是对于 Fibonacci 问题,我们需要前两个值。

我们怎样才能实现它?

solving fibonacci (non recursive way)

您的方法不会发生这种情况

基于前两个数的斐波那契数的生成是基于前两个数的,即它是一个递归算法,即使你实现它没有递归但在一个循环。

还有其他基于矩阵指数的方法,因此您可以计算第 n 个斐波那契数而无需计算前 n-1 个数,但对于您的问题(计算级数),这没有意义。

所以,最后回答你的问题,即 我如何在前两个元素上使用 Lambda 表达式?:有一个元组列表,每个元组包含两个连续的数字,并对其进行迭代,每一步添加一个新元组。

最简单的解决方案是使用 Pairs 的流:

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

由于没有标准的pair类型,所以使用二元数组。此外,我使用 .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]));

这将 运行 直到您没有足够的内存来表示下一个值。

顺便说一下,要从流中获取第 n 个元素:

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

获取第 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 - 生成数字对,每个数字包含两个连续的斐波那契元素。我们必须使用对,因为我们 只能通过“迭代”访问最后一个元素,而不是两个或更多 以前的元素,所以要生成一个新的对,我们得到最后一对, 它已经包含斐波那契的两个先前元素,并产生 下一对。而要得到第 N 个斐波那契元素,我们只需要 从第 N 对中获取左边的值。

  • .limit(n) - 保留前 N 对,并排除其余的。

  • .reduce((a, b) -> b) - 从上一步的 N 对流中获取最后一对.

  • .get()[0] - 从对中提取斐波那契元素(对的左值)

您可以在 lambda 表达式中使用变量来临时存储前一个元素,这是计算斐波那契数列中下一个元素所需要的。

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

通常情况下,方法和字段宁愿声明为静态的,但我想表明实例字段也可以使用。

请注意,您不能使用局部变量(在方法中声明或传递给方法)代替字段,因为此类变量必须是最终变量才能在 lambda 中使用它们。出于我们的目的,我们需要一个可变变量来存储迭代期间的不同值。

我知道这是一个老问题,但我觉得值得提出更多使用 Pair<> 实现的方法,我想出了两种使用 Stream API 实现的方法。

//calculate Fibonacci at given place
public static long fibonacciAt(int place) {
    Pair<Integer, Integer> seed = new Pair<>(0, 1);
    //return Stream.iterate(seed, feed -> new Pair<>(feed.getValue(), feed.getValue() + feed.getKey())).limit(place).reduce((integerIntegerPair, integerIntegerPair2) -> integerIntegerPair2).orElse(seed).getValue();
    return Stream.iterate(seed, feed -> new Pair<>(feed.getValue(), feed.getValue() + feed.getKey())).limit(place).skip(place-1).findFirst().orElse(seed).getValue();
}

评论的 return 语句也可以正常使用 reduce

后面的return语句是跳过place变量的对数。(这是因为我们没有findLast()方法)。

您可以将 n-th 斐波那契数的计算视为使用 两个 个先前元素而不是一个典型元素的减少。
这是一些代码:

public static long fibonacciByStream(int n) {
    long[] results = IntStream.rangeClosed(3, n)
            .boxed()
            .reduce(new long[]{0, 1, 1},
                    (fib, i) -> {
                        fib[i % 3] = fib[(i - 2) % 3] + fib[(i - 1) % 3];
                        return fib;
                    },
                    (a, b) -> null);
    return results[n % 3];
}

但是,这个解决方案看起来更简单没有任何流:

public static long fibonacciByLoop(int n) {
    long[] fib = new long[]{0, 1, 1};
    for (int i = 3; i <= n; i++) {
        fib[i % 3] = fib[(i - 2) % 3] + fib[(i - 1) % 3];
    }
    return fib[n % 3];
}

备注:

  • 我认为 0 是第 0 个斐波那契数,因为它使代码更简单。
  • 我使用了一个包含三个元素而不是两个元素的数组,因为它更容易理解。
  • 我使用“(a, b) -> null”作为组合器,因为它不在顺序流中使用。
  • 我省略了检查 n 是否不为负数。