斐波那契javascript实现比较

I had some free time and was going through some programming concepts and came across the famous Fibonacci sequence.

我有一些空闲时间,正在研究一些编程概念,并遇到了著名的斐波那契数列

Although I didn’t know the exact formula, I knew it was possible to calculate a specific number of the sequence using the golden ratio. While I was searching for this I didn’t find any computing comparison between that implementation and “the other” ones. So I decided to spend some time doing it myself. If nothing else, I hope this article helps somebody crack this eternal interview question.

尽管我不知道确切的公式,但我知道可以使用黄金比率来计算特定数量的序列。 在搜索此内容时,我没有发现该实现与“其他”实现之间的任何计算比较。 所以我决定花一些时间自己做。 如果没有别的,我希望本文能帮助某人破解这个永恒的面试问题。

In this article, I’m going to explore different implementations and compare their execution time using NodeJS. Those implementation being:

在本文中,我将探索不同的实现并使用NodeJS比较它们的执行时间。 这些实现是:

  • Recursive

    递归的
  • Recursive with memoization

    带记忆递归
  • Dynamic programing

    动态编程
  • Golden Ratio

    黄金比例

递归实施(Recursive Implementation)

const fibonacci = (n) => {
    if (n<=1) return 1;
    return fibonacci(n-1)+fibonacci(n-2);
}

No Rocket Science here, If you are familiar with recursion this is a quite simple approach. However, once the inputs get bigger JS callstack will start to suffer. Notice, for example, to execute fibonacci(5), the algorithm executes fibonacci(3) 3 times. See the illustration below.

这里没有火箭科学,如果您熟悉递归,这是一种非常简单的方法。 但是,一旦输入变大,JS调用堆栈将开始受到影响。 注意,例如,要执行fibonacci(5),该算法将执行fibonacci(3)3次。 请参见下图。

Image for post

带记忆递归 (Recursive with memoization)

To mitigate the previous issue we can improve the current solution by adding a simple memory state that prevents calculating the same value twice. A simple cache management.

为了减轻先前的问题,我们可以通过添加一个简单的内存状态来改进当前解决方案,该内存状态可以防止两次计算相同的值。 简单的缓存管理。

const fibonacci = (n, memory) => {
    memory = memory || {};
    if (memory[n]) return memory[n];
    if (n <= 1) return 1;
  
    return memory[n] = fibonacci(n - 1, memory) + fibonacci(n - 2, memory);
}

This approach improves the executing times but increases the memory usage since we are storing all the previous values in our memory. Once we begin increasing the input the memory allocate will have to increase for a certain threshold we going to start developing memory issues. Notice that we use recursion mechanism so callstack in be impacted as well.

由于我们将所有先前的值存储在内存中,因此该方法可以缩短执行时间,但会增加内存使用量。 一旦开始增加输入,内存分配将必须增加一定的阈值,我们才能开始开发内存问题。 注意,我们使用了递归机制,因此调用堆栈也将受到影响。

动态编程 (Dynamic programming)

Dynamic programming approach is a bit different. It operates using an up-bottom. Dynamic programming because we use a pool of variables and there is no memory increase from the first iteration until the last.

动态编程方法有些不同。 它使用上底操作。 动态编程,因为我们使用了一个变量池,并且从第一次迭代到最后一次迭代都没有增加内存。

const fibonacci = (n) => {
    let a = 1, b = 0, temp;
    
    while (n >= 0) {
      temp = a;
      a = a + b;
      b = temp;
      n--;
    }
    
    return b;
}

Since the approach is 100% iterative, there is no recursion so callstack won’t suffer. This approach will work for every input (although we might be restricted by maximum integer value).

由于该方法是100%迭代的,因此没有递归,因此调用堆栈不会受到影响。 这种方法将适用于每个输入(尽管我们可能会受到最大整数值的限制)。

黄金比例 (Golden Ratio)

Golden Ratio calculation, as the name indicates, allows us to calculate fibonacci sequences for a specific index just using a calculation. These are some websites I used as a source: Fibonacci.com & MathIsFun.com.

顾名思义,黄金比率计算使我们能够仅使用计算来计算特定索引的斐波纳契数列。 这些是我用作来源的一些网站: Fibonacci.comMathIsFun.com

const fibonacci = (n) => {
    const phi = (Math.sqrt(5)+1)/2;
    return Math.round((Math.pow(phi,n+1) - (Math.pow(-1,n+1) / Math.pow(phi,n+1))) / Math.sqrt(5));
}

This calculation is linear. We do the same calculation. Although, executing time might very a bit depending on each calculation (remember javascript is not optimised for complex number operations).

该计算是线性的。 我们进行相同的计算。 虽然,执行时间可能会略有不同,具体取决于每次计算(切记javascript没有针对复数运算进行优化)。

结果 (Results)

A GitHub repo with all this case can be found here.

可以在这里找到有关所有这种情况的GitHub存储库。

I used NodeJS process.hrtime() to calculate the execution time for each algorithm. Also exported the results into .csv files so I easily create graphs using a simple google sheet.

我使用NodeJS process.hrtime()来计算每种算法执行时间还将结果导出到中。 csv文件,因此我可以使用简单的Google表格轻松创建图形。

Also create a small test function to guarantee all implementations were correct.

还创建一个小的测试函数,以确保所有实现都是正确的。

To improve visibility, I had to create 3 different datasets comparing these implementations:

为了提高可见性,我不得不创建3个不同的数据集来比较这些实现:

  • Inputs to 40

    输入到40
  • Inputs to 15k

    输入到15k
  • Inputs to 10M

    输入到10M

Notice: All execution time values are in milliseconds.

注意:所有执行时间值均以毫秒为单位。

输入到40 (Inputs to 40)

You can check the results here

您可以在这里查看结果

Image for post

In this graphic we can see that the simple recursive solution execution time rapidly increases. This was expected. Let’s see what happens for further bigger inputs.

在此图中,我们可以看到简单的递归解决方案执行时间Swift增加。 这是预期的。 让我们看看更大的输入会发生什么。

输入到15k (Inputs to 15k)

For this data set I remove the simple recursive implementation.

对于此数据集,我删除了简单的递归实现。

You can check the results here

您可以在这里查看结果

Image for post

There is a massive spike for the memory approach around the 1k input and again when the input reached 7.5k. Another one for the dynamic approach around 10k, I can’t explain those for now.

在1k输入附近以及当输入达到7.5k时,内存方法都会出现大量峰值。 另一种适用于10k左右的动态方法,我现在无法解释。

But one thing happened, we started having callstack issues once the input reached 12.5k. This was the threshold limit for my setup. I guess I could increase the stack size to allow executions to proceed but that’s not ideal.

但是发生了一件事情,一旦输入达到12.5k,我们就开始出现调用堆栈问题。 这是我设置的阈值限制。 我想我可以增加堆栈大小以允许执行执行,但这并不理想。

输入到10M (Inputs to 10M)

For this dataset I only used the dynamic approach and the golden ratio calculation. This is when things start to get interesting. Result data can be found here.

对于此数据集,我仅使用了动态方法和黄金分割率计算。 这是事情开始变得有趣的时候。 结果数据可以在这里找到。

Image for post

Dynamic approach worked fine for 10M. I’m confident the dynamic approach is going to work as long as we don’t reach the maximum integer value.

动态方法适用于10M。 我有信心只要不达到最大整数值,动态方法就能奏效。

However, comparing the executing time from both dynamic(14334768ms) and golden ratio(614ms) we observe a reduction of 23k times!

但是,比较动态(14334768ms)和黄金比率(614ms)的执行时间,我们发现减少了23k倍!

结论 (Conclusion)

As we expected:

如我们所料:

  • Simple recursive approach only works for a very limited input;

    简单的递归方法仅适用于非常有限的输入。

  • Recursive approach with cache works a lot better, however call stack issues are going to be there;

    带有缓存的递归方法效果要好得多,但是调用堆栈问题将会出现。

  • Dynamic approach execution time increases linear with the input but is still able to provide results for a massive number

    动态方法执行时间随输入线性增加,但仍然能够提供大量结果

  • Golden ratio approach provides a solution that is almost linear regarding execution time.

    黄金分割法提供了一种在执行时间方面几乎是线性的解决方案。

Personally, I don’t recommend anybody to do big numbers operations using JS!

我个人不建议任何人使用JS进行大数运算!

In case you’re interested, I created a GitHub repo with all the implementations and auxiliary functions to create .csv files, so anybody can play around. Check it out.

如果您有兴趣,我创建了一个GitHub存储库,其中包含所有实现和辅助功能以创建.csv文件,因此任何人都可以玩。 看看这个。

Most probably, nobody is going to ask you to develop a fibonacci number calculator. Nevertheless, I won’t lie, I had some fun exploring this.

最有可能的是,没有人会要求您开发斐波那契数计算器。 尽管如此,我不会撒谎,我在探索这个过程中获得了一些乐趣。

Hope you found this interesting, in some form.

希望您以某种形式发现了这一有趣之处。

Many thanks.

非常感谢。

Image for post
Subscribe to Decoded, the official In Plain English YouTube channel 订阅Decoded,官方的普通英语YouTube频道

翻译自: https://medium.com/@joaomoliveira.gomes/fibonacci-javascript-implementations-comparison-fd09190f5b79

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值