斐波那契序列递归方法

这篇文章涉及我最喜欢的三个主题-数学,通过经验传递知识(教程单元测试)和研究的重要性。

大多数开发人员都通过面试来了解斐波那契数列 。

为了简要回顾该系列,定义了:

F( n )= F( n-1 )+ F( n-2 ),n> 2
F(1)= F(2)= 1

有一个变体定义:

F( n )= F( n-1 )+ F( n-2 ),n> 1
F(1)= 1
F(0)= 0

白板问题“编写代码以计算F(n)”有四个众所周知的解决方案。

递归 –您需要提及此点以表明您对递归感到满意,但还必须提及这是一个非常糟糕的主意,因为它需要O(2 n时间和空间堆栈,因为您需要为每个n加倍工作。

带记忆的递归 –如果您指出这是一个很好的概括,那么这可能是一个好方法。 基本上是递归,但是您需要维护一个缓存(备注),因此只需要进行一次递归调用–后续的递归调用只查找缓存的值。 这是一种灵活的技术,因为它可用于任何纯递归函数。 (也就是说,一个仅依赖于其输入并且没有副作用的递归函数。)第一次调用需要O(n)时间,堆栈和堆空间。 我不记得您是否先对较小或较大的值进行递归调用是否重要。

如果您有持久性缓存,则后续调用需要O(1)时间,堆栈空间和O(n)堆空间。

迭代 –如果您无法缓存值(或仅想有效地初始化缓存),则可以使用迭代方法。 它需要O(n)时间,但只需要O(1)堆栈和堆空间。

直接逼近 –最后使用φ或使用sqrt(5)进行变体是众所周知的逼近。 时间,堆栈空间和堆空间为O(1) 。 如果您1)将查找表用于最小值,以及2)确保n不太大,则这是一种好方法。

最后一点经常被忽略。 只要您不超过浮点数的精度,近似值就起作用。 F( 100,000 )应该很好。 F( 1,000,000,000,000 )可能不是。 对于如此庞大的数字,迭代方法不切实际。

研究

您是否知道还有另外两个在时间和空间上具有O(lg(n))性能的解决方案(根据Wikipedia)? (我不认为它是O(lg(n)),因为它不是分治法-两个递归调用不会将它们的初始工作分割开来-但是凭记住,它肯定小于O(n) 。我怀疑,但无法Swift证明它是O(lg 2 (n)) 。)

根据维基百科,我们知道:

F( 2n-1 )= F 2n )+ F 2n-1
F( 2n )= F( n )(F( n )+ 2F( n-1 ))

将其重写为F( n )的递归方法很简单。

还有一个考虑三种情况的属性-F( 3n-2 ),F( 3n-1 )和F( 3n )。 有关详细信息,请参见代码。

这些位点提供了斐波那契和相关卢卡斯序列的许多其他属性。 很少有开发人员会需要了解这些属性,但是在极少数情况下,一小时的研究可以节省数天的工作。

实作

现在,我们可以使用我们的研究为斐波那契和卢卡斯序列实施合适的方法。

斐波那契计算

(此代码未显示对n足够小的未缓存值使用直接逼近的优化。)

/*** Get specified Fibonacci number.* @param n* @return*/@Overridepublic BigInteger get(int n) {if (n < 0) {throw new IllegalArgumentException("index must be non-negative");}BigInteger value = null;synchronized (cache) {value = cache.get(n);if (value == null) {int m = n / 3;switch (n % 3) {case 0:value = TWO.multiply(get(m).pow(3)).add(THREE.multiply(get(m + 1)).multiply(get(m)).multiply(get(m - 1)));break;case 1:value = get(m + 1).pow(3).add(THREE.multiply(get(m + 1).multiply(get(m).pow(2)))).subtract(get(m).pow(3));break;case 2:value = get(m + 1).pow(3).add(THREE.multiply(get(m + 1).pow(2).multiply(get(m)))).add(get(m).pow(3));break;}cache.put(n, value);}}return value;}

斐波那契迭代器

/*** ListIterator class.* @author bgiles*/private static final class FibonacciIterator extends ListIterator {private BigInteger x = BigInteger.ZERO;private BigInteger y = BigInteger.ONE;public FibonacciIterator() {}public FibonacciIterator(int startIndex, FibonacciNumber fibonacci) {this.idx = startIndex;this.x = fibonacci.get(idx);this.y = fibonacci.get(idx + 1);}protected BigInteger getNext() {BigInteger t = x;x = y;y = t.add(x);return t;}protected BigInteger getPrevious() {BigInteger t = y;y = x;x = t.subtract(x);return x;}}

卢卡斯计算

/*** Get specified Lucas number.* @param n* @return*/public BigInteger get(int n) {if (n < 0) {throw new IllegalArgumentException("index must be non-negative");}BigInteger value = null;synchronized (cache) {value = cache.get(n);if (value == null) {value = Sequences.FIBONACCI.get(n + 1).add(Sequences.FIBONACCI.get(n - 1));cache.put(n, value);}}return value;}

Lucas迭代器

/*** ListIterator class.* @author bgiles*/private static final class LucasIterator extends ListIterator {private BigInteger x = TWO;private BigInteger y = BigInteger.ONE;public LucasIterator() {}public LucasIterator(int startIndex, LucasNumber lucas) {idx = startIndex;this.x = lucas.get(idx);this.y = lucas.get(idx + 1);}protected BigInteger getNext() {BigInteger t = x;x = y;y = t.add(x);return t;}protected BigInteger getPrevious() {BigInteger t = y;y = x;x = t.subtract(x);return x;}}

教育

教育其他开发人员有关这些意外关系的存在的最佳方法是什么? 代码,当然!

对其他开发人员进行有关存在可证明这些关系的代码的教育的最佳方法是什么? 当然,单元测试!

编写单元测试可以同时验证我们的实现并通知其他开发人员可以用来改进代码的技巧,这很简单。 关键是提供指向其他信息的链接。

斐波那契数列

public class FibonacciNumberTest extends AbstractRecurrenceSequenceTest {private static final BigInteger MINUS_ONE = BigInteger.valueOf(-1);/*** Constructor*/public FibonacciNumberTest() throws NoSuchMethodException {super(FibonacciNumber.class);}/*** Get number of tests to run.*/@Overridepublic int getMaxTests() {return 300;}/*** Verify the definition is properly implemented.** @return*/@Test@Overridepublic void verifyDefinition() {for (int n = 2; n < getMaxTests(); n++) {BigInteger u = seq.get(n);BigInteger v = seq.get(n - 1);BigInteger w = seq.get(n - 2);Assert.assertEquals(u, v.add(w));}}/*** Verify initial terms.*/@Test@Overridepublic void verifyInitialTerms() {verifyInitialTerms(Arrays.asList(ZERO, ONE, ONE, TWO, THREE, FIVE, EIGHT));}/*** Verify that every third term is even and the other two terms are odd.* This is a subset of the general divisibility property.** @return*/@Testpublic void verifyEvenDivisibility() {for (int n = 0; n < getMaxTests(); n += 3) {Assert.assertEquals(ZERO, seq.get(n).mod(TWO));Assert.assertEquals(ONE, seq.get(n + 1).mod(TWO));Assert.assertEquals(ONE, seq.get(n + 2).mod(TWO));}}/*** Verify general divisibility property.** @return*/@Testpublic void verifyDivisibility() {for (int d = 3; d < getMaxTests(); d++) {BigInteger divisor = seq.get(d);for (int n = 0; n < getMaxTests(); n += d) {Assert.assertEquals(ZERO, seq.get(n).mod(divisor));for (int i = 1; (i < d) && ((n + i) < getMaxTests()); i++) {Assert.assertFalse(ZERO.equals(seq.get(n + i).mod(divisor)));}}}}/*** Verify the property that gcd(F(m), F(n)) = F(gcd(m,n)). This is a* stronger statement than the divisibility property.*/@Testpublic void verifyGcd() {for (int m = 3; m < getMaxTests(); m++) {for (int n = m + 1; n < getMaxTests(); n++) {BigInteger gcd1 = seq.get(m).gcd(seq.get(n));int gcd2 = BigInteger.valueOf(m).gcd(BigInteger.valueOf(n)).intValue();Assert.assertEquals(gcd1, seq.get(gcd2));}}}/*** Verify second identity (per Wikipedia): sum(F(i)) = F(n+2)-1*/@Testpublic void verifySecondIdentity() {BigInteger sum = ZERO;for (int n = 0; n < getMaxTests(); n++) {sum = sum.add(seq.get(n));Assert.assertEquals(sum, seq.get(n + 2).subtract(ONE));}}/*** Verify third identity (per Wikipedia): sum(F(2i)) = F(2n+1)-1 and* sum(F(2i+1)) = F(2n)*/@Testpublic void verifyThirdIdentity() {BigInteger sum = ZERO;for (int n = 0; n < getMaxTests(); n += 2) {sum = sum.add(seq.get(n));Assert.assertEquals(sum, seq.get(n + 1).subtract(ONE));}sum = ZERO;for (int n = 1; n < getMaxTests(); n += 2) {sum = sum.add(seq.get(n));Assert.assertEquals(sum, seq.get(n + 1));}}/*** Verify fourth identity (per Wikipedia): sum(iF(i)) = nF(n+2) - F(n+3) + 2*/@Testpublic void verifyFourthIdentity() {BigInteger sum = ZERO;for (int n = 0; n < getMaxTests(); n++) {sum = sum.add(BigInteger.valueOf(n).multiply(seq.get(n)));BigInteger x = BigInteger.valueOf(n).multiply(seq.get(n + 2)).subtract(seq.get(n + 3)).add(TWO);Assert.assertEquals(sum, x);}}/*** Verify fifth identity (per Wikipedia): sum(F(i)^2) = F(n)F(n+1)*/public void verifyFifthIdentity() {BigInteger sum = ZERO;for (int n = 0; n < getMaxTests(); n += 2) {BigInteger u = seq.get(n);BigInteger v = seq.get(n + 1);sum = sum.add(u.pow(2));Assert.assertEquals(sum, u.multiply(v));}}/*** Verify Cassini's Identity - F(n-1)F(n+1) - F(n)^2 = -1^n*/@Testpublic void verifyCassiniIdentity() {for (int n = 2; n < getMaxTests(); n += 2) {BigInteger u = seq.get(n - 1);BigInteger v = seq.get(n);BigInteger w = seq.get(n + 1);BigInteger x = w.multiply(u).subtract(v.pow(2));Assert.assertEquals(ONE, x);}for (int n = 1; n < getMaxTests(); n += 2) {BigInteger u = seq.get(n - 1);BigInteger v = seq.get(n);BigInteger w = seq.get(n + 1);BigInteger x = w.multiply(u).subtract(v.pow(2));Assert.assertEquals(MINUS_ONE, x);}}/*** Verify doubling: F(2n-1) = F(n)^2 + F(n-1)^2 and F(2n) =* F(n)(F(n-1)+F(n+1)) = F(n)(2*F(n-1)+F(n).*/@Testpublic void verifyDoubling() {for (int n = 1; n < getMaxTests(); n++) {BigInteger u = seq.get(n - 1);BigInteger v = seq.get(n);BigInteger w = seq.get(n + 1);BigInteger x = v.multiply(v).add(u.pow(2));Assert.assertEquals(seq.get((2 * n) - 1), x);x = v.multiply(u.add(w));Assert.assertEquals(seq.get(2 * n), x);x = v.multiply(v.add(TWO.multiply(u)));Assert.assertEquals(seq.get(2 * n), x);}}/*** Verify tripling.*/@Testpublic void verifyTripling() {for (int n = 1; n < getMaxTests(); n++) {BigInteger u = seq.get(n - 1);BigInteger v = seq.get(n);BigInteger w = seq.get(n + 1);BigInteger x = TWO.multiply(v.pow(3)).add(THREE.multiply(v).multiply(u).multiply(w));Assert.assertEquals(seq.get(3 * n), x);x = w.pow(3).add(THREE.multiply(w).multiply(v.pow(2))).subtract(v.pow(3));Assert.assertEquals(seq.get((3 * n) + 1), x);x = w.pow(3).add(THREE.multiply(w.pow(2)).multiply(v)).add(v.pow(3));Assert.assertEquals(seq.get((3 * n) + 2), x);}}
}

卢卡斯序列

public class LucasNumberTest extends AbstractRecurrenceSequenceTest {private static final FibonacciNumber fibonacci = new FibonacciNumber();/*** Constructor*/public LucasNumberTest() throws NoSuchMethodException {super(LucasNumber.class);}/*** Get number of tests to run.*/@Overridepublic int getMaxTests() {return 300;}/*** Verify the definition is properly implemented.** @return*/@Test@Overridepublic void verifyDefinition() {for (int n = 2; n < getMaxTests(); n++) {BigInteger u = seq.get(n);BigInteger v = seq.get(n - 1);BigInteger w = seq.get(n - 2);Assert.assertEquals(u, v.add(w));}}/*** Verify initial terms.*/@Test@Overridepublic void verifyInitialTerms() {verifyInitialTerms(Arrays.asList(TWO, ONE, THREE, FOUR, SEVEN, ELEVEN,BigInteger.valueOf(18), BigInteger.valueOf(29)));}/*** Verify Lucas properties.*/@Testpublic void verifyLucas() {// L(n) = F(n-1) + F(n+1)for (int n = 2; n < getMaxTests(); n++) {Assert.assertEquals(seq.get(n),fibonacci.get(n - 1).add(fibonacci.get(n + 1)));}}/***  F(2n) = L(n)F(n)*/@Testpublic void verifyLucas2() {for (int n = 2; n < getMaxTests(); n++) {Assert.assertEquals(fibonacci.get(2 * n),seq.get(n).multiply(fibonacci.get(n)));}}/*** F(n) = (L(n-1)+ L(n+1))/5*/@Testpublic void verifyLucas3() {for (int n = 2; n < getMaxTests(); n++) {Assert.assertEquals(FIVE.multiply(fibonacci.get(n)),seq.get(n - 1).add(seq.get(n + 1)));}}/*** L(n)^2 = 5 F(n)^2 + 4(-1)^n*/@Testpublic void verifyLucas4() {for (int n = 2; n < getMaxTests(); n += 2) {Assert.assertEquals(seq.get(n).pow(2),FIVE.multiply(fibonacci.get(n).pow(2)).add(FOUR));}for (int n = 1; n < getMaxTests(); n += 2) {Assert.assertEquals(seq.get(n).pow(2),FIVE.multiply(fibonacci.get(n).pow(2)).subtract(FOUR));}}
}

结论

显然,除非开发人员正在处理Project Euler问题或进行工作面试,否则开发人员几乎不需要计算斐波纳契数。 这段代码不会直接使用。

同时,这是有力的证据表明,即使您确定已经了解了所有需要了解的知识,还是要花一两个小时进行研究。 您可能不需要BigInteger实现,但是有些人可能认为O(lg(n))方法优于使用φ的幂进行估计,或者可以充分利用MathWorld和Wikipedia页面上讨论的关系。

源代码

好消息是我已经为此发布了源代码……坏消息是当我处理Project Euler问题时,这是正在进行的涂鸦的一部分。 (这里没有解决方案-完全是对问题启发的思想探索。因此,代码有点粗糙,因此不应用于决定是否邀请我参加面试(除非给您留下深刻的印象): http ://github.com/beargiles/projecteuler 。

翻译自: https://www.javacodegeeks.com/2014/06/fibonacci-and-lucas-sequences.html

斐波那契序列递归方法

斐波那契序列递归方法_斐波那契和卢卡斯序列相关推荐

  1. 波菲那契数列公式_斐波那契数列为什么那么重要,所有关于数学的书几乎都会提到?...

    一句话先回答问题:因为斐波那契数列在数学和生活以及自然界中都非常有用. 下面我就尽我所能,讲述一下斐波那契数列. 一.起源和定义 斐波那契数列最早被提出是印度数学家Gopala,他在研究箱子包装物件长 ...

  2. 斐波纳契回调线_斐波那契回调线(黄金分割线)神级操作-经典

    斐波那契回调线(黄金分割线)神级操作-经典 斐波那契回调线,又称黄金分割线.在交易市场上,大多数的技术指标都具有滞后性,导致交易者在使用时不太好掌握.但是,斐波那契回调线具有提前性,能很好的帮助交易者 ...

  3. 斐波纳契回调线_斐波那契回调线(黄金分割线)全面解析

    一.斐波那契数列 斐波那契数列是指这样一个数列:1.1.2.3.5.8.13.21.34--,这个数列从第三项开始,每一项都等于前两项之和.它的通项公式为F(n)=(1/√5)*{[(1+√5)/2] ...

  4. 小波自适应阈值选取python_小波去噪阈值如何选取_小波阈值分析 - 全文

    1.小波阈值去噪法的流程如下所示: 小波图像去噪就是根据信号和噪声的小波系数在不同尺度上具有不同性质的原理,利用相应的数学工具构造系数选择方式,对带噪信号的小波系数进行处理.小波去噪过程就是利用小波分 ...

  5. 单片机c语言小波阈值降噪,小波阈值去噪的基本原理_小波去噪阈值如何选取

    小波阈值去噪的基本原理 小波阈值去噪的基本思想是先设置一个临界阈值λ,若小波系数小于λ,认为该系数主要由噪声引起,去除这部分系数;若小波系数大于λ,则认为此系数主要是由信号引起,保留这部分系数,然后对 ...

  6. python查询斐波那契数列通项公式_斐波那契数列求解总结(Python版)

    最近在查阅斐波那契数列时,看到下面的文章,总结得非常好,于是自己上手使用 Python 练习并实现多种求解方法 守望:面试官问你斐波那契数列的时候不要高兴得太早​zhuanlan.zhihu.com ...

  7. java斐波那契数列公式_斐波那契数列(公式)

    求大数前几位的方法 当一个数非常大时,如何求出其前几位呢? 如果是给定一个特定的数,当然可以逐步取出每一位即可.如 a得个位,a/10得百位,a/10/10得千位. 但是,当求x^y的前几位时怎么办呢 ...

  8. 斐波纳契回调线_斐波那契回调线

    斐波那契回调线 英文全称为Fibonacci analysis,简称FIB,也被称作"黄金分割线",是通过分析技术图形来预判未来价格走势的一种常用工具.斐波那契回调线通常被用作分析 ...

  9. 波菲那契数列公式_斐波那契数列求和公式

    展开全部 1.奇数项求和 2.偶数项求和 3.平方求和 在数学上,斐波那契数列以如下被以32313133353236313431303231363533e78988e69d83313334313663 ...

最新文章

  1. 自然语言处理hanlp的入门基础
  2. 小师妹学JVM之:JVM中的Safepoints
  3. 码农不重视文档:开源项目深受其苦
  4. android蓝牙通信_Flutter通过BasicMessageChannel实现Flutter 与Android iOS 的双向通信
  5. Bzoj 1926: [Sdoi2010]粟粟的书架(二分答案+乱搞+主席树)
  6. python installer 在 mac 运行_python – 如何在Mac OS X 10.7中的virtualenv中安装PyAudio
  7. IP地址和子网划分学习笔记之《预备知识:进制计数》
  8. j2me模拟器自动退出的原因
  9. 在linux系统如何获得窗口句柄,编写控制台程序,获得标准输入输出的窗口句柄...
  10. Python渗透测试工具合集
  11. war3鸿蒙大陆攻略,【魔兽rpg地图失落大陆攻略汇总】
  12. 分时线的9代表什么_一位从亏损到稳赚的老股民告诉你:为什么要打板?
  13. 有趣的微分方程之齐次方程
  14. 各种水龙头拆卸图解_各种水龙头拆卸图解
  15. 【本科课程学习】数据库考试复习题(带答案)
  16. 图片阴影怎么设置_电影大片风格!教你用PS调出胶片质感的图片
  17. 《计算机达人成长之路——憧憬与迷茫篇》有钱的捧个预订场,有人的捧个评价场...
  18. 不讲周期长短不足以谈牛熊
  19. iSCSI target介绍及LIO实操入门
  20. html实现凹陷效果,css3怎么实现字体凹陷凸出效果?(附代码)

热门文章

  1. (转)图解如何制作网线
  2. RAM、ROM、emmc、iNand、SD卡、mmc 与 Nandflash 的区别
  3. 钉钉电脑端屏蔽文件上传下载功能的方法
  4. step5.游戏窗口的初始化
  5. 系综理论(Ensemble Theory)
  6. 一个简单的安居客房屋信息爬虫
  7. PyTorch DDP
  8. 简单三步搭建公司内部论坛:安装部署Discuz 1-2
  9. 科技部原副部长吴忠泽:尽早抢占元宇宙高地,掌握下一代互联网的话语权和主动权
  10. 【Vue element-admin 如何给侧边栏添加 Badge 计数标记 】