用于求解斐波那契的 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】:
最简单的解决方案是使用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]));
由于缺少标准的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 表达式(非递归方式)的主要内容,如果未能解决你的问题,请参考以下文章