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

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

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

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. 斐波那契序列递归方法_斐波那契和卢卡斯序列

    斐波那契序列递归方法 这篇文章涉及我最喜欢的三个主题-数学,通过经验传递知识(教程单元测试)和研究的重要性. 大多数开发人员都通过面试来了解斐波那契数列 . 为了简要回顾该系列,定义了: F( n ) ...

  2. 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...

    function fb(n) {if (n === 1 || n === 2) {return 1;}return fb(n - 1) + fb(n - 2);}// 用户输入一个数字 n 就可以求出 ...

  3. 《每日一题》842. Split Array into Fibonacci Sequence 将数组拆分成斐波那契序列

    给定一个数字字符串 S,比如 S = "123456579",我们可以将它分成斐波那契式的序列 [123, 456, 579]. 形式上,斐波那契式序列是一个非负整数列表 F,且满 ...

  4. leetcode 842. 将数组拆分成斐波那契序列(回溯算法)

    给定一个数字字符串 S,比如 S = "123456579",我们可以将它分成斐波那契式的序列 [123, 456, 579]. 形式上,斐波那契式序列是一个非负整数列表 F,且满 ...

  5. leetcode842. 将数组拆分成斐波那契序列(回溯)

    给定一个数字字符串 S,比如 S = "123456579",我们可以将它分成斐波那契式的序列 [123, 456, 579]. 形式上,斐波那契式序列是一个非负整数列表 F,且满 ...

  6. LeetCode 842. 将数组拆分成斐波那契序列(暴力查找)

    1. 题目 给定一个数字字符串 S,比如 S = "123456579",我们可以将它分成斐波那契式的序列 [123, 456, 579]. 形式上,斐波那契式序列是一个非负整数列 ...

  7. 算法-斐波那契数列:兔子序列

    利用递归函数求斐波那契数列(兔子序列) 1.1.2.3.5.8.13.21-求第n个数是几,n任意一个数大家可以自定义,比如10,20等 分析: 从给出的数列中,可知, n=1时→1 n=2时→1 n ...

  8. c语言斐波那契数列_数学之美系列图集——斐波那契数列

    斐波那契螺旋 抽象的技术背景上的数字黄金比例 鹦鹉螺壳数学螺旋带蓝色覆盖双色调 橙. 柠檬无限螺旋抽象背景.斐波那契 鹦鹉螺壳部分,白色完美的斐波那契模式 鹦鹉螺的壳和著名的斐波那契蓝色几何图案 斐波 ...

  9. 【小组专题三:斐波那契专题】斐波那契 与其20个性质 | 泽肯朵夫表示 | 卢卡斯数 与其8个性质 | 常系数二次线性齐次序列 | 模板与例题

    斐波那契专题 斐波那契序列的定义 斐波那契序列的基本性质 卢卡斯数 卢卡斯数的基本性质 正整数的泽肯朵夫(Zeckendorf)表示 其他斐波那契的性质 常系数的二次线性齐次递归关系 斐波那契进制/斐 ...

  10. ACM_无聊者序列(斐波那契数列大数取余(同余)+规律)

    Problem Description: 瓜瓜在玩着由红色和蓝色的大理石做成的玻璃珠,他将n个玻璃珠从左到右排成一个序列叫做无聊者序列.一个非空的红色和蓝色玻璃珠组成的序列是一个无聊者序列.这个序列的 ...

最新文章

  1. 奇葩错误:cv.imread()读取失败
  2. 用 Git 和 Github 提高效率的 10 个技巧!
  3. 一个爬虫的故事:爬虫兄弟要活不下去了!!!
  4. 2道面试题:输入URL按回车HTTP2
  5. react-native 安卓支持 gif动态图
  6. SAP自定义查询工具SQVI
  7. 22 Python IO、打印到屏幕、读取键盘输入、打开和关闭文件、文件定位、重命名和删除文件、Python里的目录、文件,目录相关的方法
  8. 菜鸟ING的博客终于开园了。
  9. leetcode python3 简单题26. Remove Duplicates from Sorted Array
  10. MyEclipse设置默认注释的格式
  11. excel 第六次人口普查_excel 第六次人口普查_第六次全国人口普查表短表
  12. Android Studio实现百度地图定位(显示经纬度和地址)
  13. k8s 的容器command用法相关
  14. 三小时学会css(菜鸟教程精华版)【中】
  15. linux压缩解压工具效率,linux压缩解压工具
  16. 20135203齐岳 信息安全系统设计基础第五周学习总结
  17. 如何使用 Python 检测和识别车牌(附 Python 代码)
  18. 最新IP地理数据库(dat文件) --别花钱了
  19. Qt QNetworkAccessManager请求返回reply内存泄漏
  20. 自制APP连接OneNET---实现数据监控和下发控制(MQTT)

热门文章

  1. 食品企业如何有效保护企业数据安全?
  2. 【转自“果壳网”微软亚洲学院】光学字符识别技术:让电脑“读”懂世界
  3. 【附源码】Python计算机毕业设计双笙映画
  4. javaee2020-教程直播-18教师个人B站直播间和B站空间地址
  5. 网络安全初入茅庐 --- 简易 sqlmap 制作(转载)
  6. 建议旅游学校开办“旅游电子商务专业”或“旅游电子商务课程”
  7. 微信下输入法在IOS和安卓下的诡异
  8. Boost库Lexical_Cast模块使用介绍
  9. html图片多边形怎么写,javascript - 如何在HTML5画布上绘制多边形?
  10. 【牛客网】今日头条2017客户端工程师实习生笔试题