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

Posted

技术标签:

【中文标题】用于求解斐波那契的 Java 8 Lambda 表达式(非递归方式)【英文标题】:Java 8 Lambda expressions for solving fibonacci (non recursive way) 【发布时间】:2015-08-16 05:33:32 【问题描述】:

我是在 Java 8 中使用 Lambda 表达式功能的初学者。Lambda 表达式在解决诸如质数检查、阶乘等程序时非常有用。

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

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

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

我们怎样才能做到这一点?

【问题讨论】:

Stream.iterate(Fibonacci.SEED, Fibonacci::next).limit(MAX_NUMS).forEach(System.out::println) 完整的code example 展示了如何使用 Java 8 生成斐波那契。最后一个。 【参考方案1】:

最简单的解决方案是使用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];

【讨论】:

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

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

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

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

【讨论】:

请解释一下迭代和归约【参考方案3】:

解决斐波那契(非递归方式)

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

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

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

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

【讨论】:

【参考方案4】:

您可以在 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 中使用。出于我们的目的,我们需要一个可变变量来存储迭代期间的不同值。

【讨论】:

【参考方案5】:

我知道这是一个老问题,但我觉得值得提出更多使用 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();

使用reduce 的注释返回语句也可以正常工作。

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

【讨论】:

【参考方案6】:

您可以将第 n 个斐波那契数的计算视为使用 两个 之前的元素而不是典型的一个元素进行缩减。 这是一些代码:

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 是否为负数。

【讨论】:

以上是关于用于求解斐波那契的 Java 8 Lambda 表达式(非递归方式)的主要内容,如果未能解决你的问题,请参考以下文章

斐波那契高效算法(4种算法综合分析)

nefu 115 斐波那契的整除

1 斐波那契的兔子

1355 斐波那契的最小公倍数

这个递归斐波那契的大 O 时间复杂度?

51nod 1355 斐波那契的最小公倍数