`
rijin
  • 浏览: 139459 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

这样实现Fibonacci最快最简单!

    博客分类:
  • Java
阅读更多
大家都知道Fibonacci数列(一般译为斐波那契数列),比如:0, 1, 1, 2, 3, 5, 8, 13, 21...这是一个通过重复计算生成数列的好例子:f(n) = f(n-2) + f(n-1)。我们可以写一个计算第n个(从0开始)Fibonacci数的简单代码:
public class Fibonacci {

    public int fib(int n) {
        if (n == 0 || n == 1) return n;

        System.out.println("calculating fib(" + n + ")");
        return fib(n - 2) + fib(n - 1);
    }

}
 
让我们打印下计算第7个Fibonacci数的:
public class Main {

    public static void main(String[] args) {
        System.out.println("fib(7) = " + new Fibonacci().fib(7));
    }

}
 
结果如下:
calculating fib(7)
calculating fib(5)
calculating fib(3)
calculating fib(2)
calculating fib(4)
calculating fib(2)
calculating fib(3)
calculating fib(2)
calculating fib(6)
calculating fib(4)
calculating fib(2)
calculating fib(3)
calculating fib(2)
calculating fib(5)
calculating fib(3)
calculating fib(2)
calculating fib(4)
calculating fib(2)
calculating fib(3)
calculating fib(2)
fib(7) = 13
 
发现没,这样计算的步骤太多太复杂了!上面代码里有着严重的性能问题:fib(n)的调用次数随着n的增长呈指数级增长。
我们可以用一个加缓存的方法来优化,当然这个缓存是要线程安全的:
public class Fibonacci {

    private Map cache = new ConcurrentHashMap<>();

    public int fib(int n) {
        if (n == 0 || n == 1) return n;

        Integer result = cache.get(n);

        if (result == null) {
            synchronized (cache) {
                result = cache.get(n);

                if (result == null) {
                    System.out.println("calculating fib(" + n + ")");
                    result = fib(n - 2) + fib(n - 1);
                    cache.put(n, result);
                }
            }
        }

        return result;
    }

}
 
计算过程和结果如下:
calculating fib(7)
calculating fib(5)
calculating fib(3)
calculating fib(2)
calculating fib(4)
calculating fib(6)
fib(7) = 13
 
上面代码会先检查下缓存里的结果。如果这步调用还没有结果的,就计算出来并且把结果放进缓存。为了更好的性能,用了一个双重检查锁定(译者注:其实这里可以展开讨论下,为什么要加锁,如果不加锁会怎么样),可惜的是代码变复杂了。
 

Java8来拯救小伙伴们了!

 

让我们看下ConcurrentHashMap在Java8里一个新方法:
public V computeIfAbsent(K key, Function mappingFunction)
 
这个ConcurrentHashMap.computeIfAbsent有什么用呢?如果在map里没找到给定的key值所对应的entry,它会调用mappingFunction(key)来计算出哈希值,然后执行put(key,value)操作,整个过程都是保证原子性的。实际上它就是替我们完成了一堆在Java8版本前我们要自己写的脏活累活。这样现在的代码就非常简单了(当然还要感谢Java8里的新特性lambda):
public class Fibonacci {

    private Map cache = new ConcurrentHashMap<>();

    public int fib(int n) {
        if (n == 0 || n == 1) return n;

        return cache.computeIfAbsent(n, (key) -> {
            System.out.println("calculating fib(" + n + ")");
            return fib(n - 2) + fib(n - 1);
        });
    }

}
 
当然,打印语句那里,还可以通过lambda继续简化:
public class Fibonacci {

    private Map cache = new ConcurrentHashMap<>();

    public int fib(int n) {
        if (n == 0 || n == 1) return n;

        return cache.computeIfAbsent(n, (key) -> fib(n - 2) + fib(n - 1));
    }

}
 
本文只是写了Java8里通过ConcurrentHashMap的一个新方法,简化了代码的小例子,其实Java8里让人点赞的新特性还有很多...
(英文原文出自这里)
本文同时发表在本人博客www.newhottopic.com  ,并非转载。
6
7
分享到:
评论
1 楼 Grumpy 2014-03-13  
递归的精髓在于其想法,你把Fibonacci写的也太复杂了吧
    public static int getFibocacci(int index) {
        int b = 0, n = 1;
        for (int i = 2; i <= index; i++) {
            if (i % 2 == 0) {
                b += n;
            } else {
                n += b;
            }
        }
        return index < 2 ? index : (b > n ? b : n);
    }

相关推荐

    fibonacci:比较Java中的三种不同的Fibonacci实现

    这是一个简单的程序,可用于比较Java中Fibonacci算法的3种不同实现,以了解计算时间。 这三个实现如下: 递归斐波那契 尾递归斐波那契 迭代斐波那契 结果表明,迭代一次是最快的,而递归一次是最慢的。 因此,递归...

    uselessness:那就是我要上传的一些代码示例的地方,尽管其中大多数都是无用的

    Fibonacci irc bot(fibirc.vala) 用vala编写的这个简单的irc机器人连接到irc通道,并使用第n个斐波那契数来回复'!fib n'命令。 编译: valac fibirc.vala --pkg gee-1.0 --pkg gio-2.0它需要valac编译器。 用法 ...

    MT4.rar_MT4指标_mt4_mt4策略_外汇;指标;_趋势

    现在,如果我们告诉您有一种简单的方法可以在价格图表上绘制斐波那契回撤水平 。 如果一切都自动完成怎么办? 好吧,我们的交易策略指南团队开发了一个专有的斐波那契黄金区 指标,一旦放在图表上,它将立即绘制 ...

    数据结构与算法研究(强烈推荐)

    通过直观地把一些简单递归程序转变成迭代程序而对它们进行分析。介绍了更为复杂的分治程序,不过有些分析(求解递归关系)要推迟到第7章再详细讨论。 第3章包括表、栈和队列。重点放在使用ADT对这些数据结构编程,...

    数据结构:八大数据结构分析.pdf

    常⽤的数据结构有:数组,栈,链表,队列,树,图,堆,散列表等,如图所⽰: 线性表和⾮线性表 ⼀、线性表 常见的线性表有:数组、队列、栈、链表 线性表是最基本、最简单、也是最常⽤的⼀种数据结构。线性表...

    C程序范例宝典(基础代码详解)

    实例030 对调最大与最小数位置 36 实例031 二维数组行列互换 37 实例032 使用数组统计学生成绩 39 实例033 打印5阶幻方 40 1.6 字符和字符串操作 41 实例034 统计各种字符个数 41 实例035 字符串倒置 ...

    Algorithm-cpp:C ++学习,算法,问题解决

    C ++学习算法时间复杂度和空间复杂度向量与队列速度比较计数/时间(毫秒) 10000 20000 30000 40000 向量202.271 804.508 1807.05 3209.99 列表3.057 5.459 7.868 10.524 队列2.356 4.6 6.837 ... 解决此问题的最简单方

    算法导论(part1)

    有一点可能是让人难以置信的,就是在本书这样一本大部头中,由于篇幅的原因,很多有趣的算法都没能包括进来。.. 尽管学生们发来了大量的请求,希望我们提供思考题和练习的解答,但我们还是决定不提供思考题和练习的...

    算法导论(part2)

    有一点可能是让人难以置信的,就是在本书这样一本大部头中,由于篇幅的原因,很多有趣的算法都没能包括进来。.. 尽管学生们发来了大量的请求,希望我们提供思考题和练习的解答,但我们还是决定不提供思考题和练习的...

    用c描述的数据结构演示软件

    (4)斐波那契查找 (Search_Fib) (5)次优查找树(BiTree_SOSTree) 11. 动态查找 (1)在二叉排序树上进行查找(bstsrch)、插入结点(ins_bstree)和删除结点(del_bstree) (2)在二叉平衡树上插入结点(ins_AVL...

    数据结构演示软件

    (4)斐波那契查找 (Search_Fib) (5)次优查找树(BiTree_SOSTree) 11. 动态查找 (1)在二叉排序树上进行查找(bstsrch)、插入结点(ins_bstree)和删除结点(del_bstree) (2)在二叉平衡树上插入结点(ins_AVL...

    c语言数据结构算法演示(Windows版)

    左下显示有向网的逻辑结构和顶点的入度及各顶点事件的最早发生时间和最迟发生时间;右下显示拓扑排序过程中入度为零的顶点的栈S,右上显示的栈 T 存放拓扑序列,其入栈顺序和栈 S 的出栈顺序相同,从栈顶到栈底的...

Global site tag (gtag.js) - Google Analytics