【问题标题】:Finding all Fibonacci numbers less than or equal to a number查找小于或等于一个数字的所有斐波那契数
【发布时间】:2021-01-30 11:35:47
【问题描述】:

我正在尝试在 JavaScript 中实现一个函数,该函数返回一个包含所有斐波那契数字的数组,直到某个数字 (num)。在我的研究过程中,我遇到了这个答案:Calculate Fibonacci numbers up to at least n。我用 JavaScript 和 Python 实现了他们的解决方案,但发现他们的解决方案有一个错误。问题是最后一个元素有时是错误的。这是我根据上面链接的答案中找到的解决方案编写的代码。

function findFibs(num) {
  
  if (num < 2) {
    return [1,1];
  } else {
    var fibs = findFibs(num - 1)
    if ((fibs[fibs.length - 1]) < num ) {
      fibs.push(fibs[fibs.length - 1] + fibs[fibs.length - 2])
    }
    return fibs;
  }
}

console.log(sumFibs(20));

这段代码的预期输出是:[ 1, 1, 3, 5, 13 ] ,但实际输出是[ 1, 1, 3, 5, 13, 21 ]。我错过了什么?

【问题讨论】:

    标签: javascript arrays recursion fibonacci


    【解决方案1】:

    功能性

    递归是一种函数式遗产,因此将其与函数式风格一起使用会产生最佳效果。这意味着要避免诸如突变、变量重新分配和其他副作用之类的事情。考虑一种更实用的方法 -

    给定一个 limit 和两个种子,ab -

    1. 如果种子a大于输入limit,则返回空结果
    2. (感应)a 小于或等于限制。重复子问题(limit, b, a + b) 并将其附加到单例[a]

    这编码为纯函数表达式 -

    const fibs = (limit, a = 1, b = 1) =>
      a > limit
        ? []                                 // #1
        : [ a, ...fibs(limit, b, a + b) ]    // #2
        
    console.log(fibs(20))
    // [ 1, 1, 2, 3, 5, 8, 13 ]

    势在必行

    如果我要在这个程序中使用命令式风格,我想我会使用生成器 -

    function* fib (a, b)
    { yield a
      yield *fib(b, a + b)
    }
    
    function* fibs (limit)
    { for (const n of fib(1, 1))
        if (n < limit)
          yield n
        else
          return
    }
    
    console.log(Array.from(fibs(20)))
    // [ 1, 1, 2, 3, 5, 8, 13 ]

    堆栈安全

    在上面的fib 中看到,我们可以使用具有命令式风格的递归,但是因为它会溢出堆栈,所以在这种情况下使用循环会更有意义 -

    function* fib (a, b)
    { let t
      while (true)    // stack-safe, but not recursive
      { yield a
        t = a
        a = b
        b = t + a
      }
    }
    
    function* fibs (limit)
    { for (const n of fib(1, 1))
        if (n < limit)
          yield n
        else
          return
    }
    
    console.log(Array.from(fibs(20)))
    // [ 1, 1, 2, 3, 5, 8, 13 ]

    BigInt

    斐波那契数列可以快速生成大量数字,JavaScript 将使用科学记数法进行近似计算。第 102nd 项是 927372692193079200000,第 103rd 项是 1.5005205362068963e+21。这是由于 JavaScript 数字的大小限制。使用较新的BigInt,我们可以轻松解决这个问题 -

    function* fib (a, b)
    { let t
      while (true)
      { yield a
        t = a
        a = b
        b = t + a
      }
    }
    
    function* fibs (limit)
    { for (const n of fib(1n, 1n)) // <- bigint
        if (n < limit)
          yield n
        else
          return
    }
    
    for (const n of fibs(1e70))
      console.log(String(n))       // <- bigint to string

    StackOverflow.com 会截断输出以仅显示最后 50 行。检查浏览器控制台的完整输出 -

    ...
    426547842461739379460149980002442288124894678853713953114433
    690168906931029935139391829792095612517948949963798093315456
    1116716749392769314599541809794537900642843628817512046429889
    1806885656323799249738933639586633513160792578781310139745345
    2923602405716568564338475449381171413803636207598822186175234
    4730488062040367814077409088967804926964428786380132325920579
    7654090467756936378415884538348976340768064993978954512095813
    12384578529797304192493293627316781267732493780359086838016392
    20038668997554240570909178165665757608500558774338041350112205
    32423247527351544763402471792982538876233052554697128188128597
    52461916524905785334311649958648296484733611329035169538240802
    84885164052257330097714121751630835360966663883732297726369399
    137347080577163115432025771710279131845700275212767467264610201
    222232244629420445529739893461909967206666939096499764990979600
    359579325206583560961765665172189099052367214309267232255589801
    581811569836004006491505558634099066259034153405766997246569401
    941390895042587567453271223806288165311401367715034229502159202
    1523202464878591573944776782440387231570435521120801226748728603
    2464593359921179141398048006246675396881836888835835456250887805
    3987795824799770715342824788687062628452272409956636682999616408
    6452389184720949856740872794933738025334109298792472139250504213
    10440185009520720572083697583620800653786381708749108822250120621
    16892574194241670428824570378554538679120491007541580961500624834
    27332759203762391000908267962175339332906872716290689783750745455
    44225333398004061429732838340729878012027363723832270745251370289
    71558092601766452430641106302905217344934236440122960529002115744
    115783425999770513860373944643635095356961600163955231274253486033
    187341518601536966291015050946540312701895836604078191803255601777
    303124944601307480151388995590175408058857436768033423077509087810
    490466463202844446442404046536715720760753273372111614880764689587
    793591407804151926593793042126891128819610710140145037958273777397
    1284057871006996373036197088663606849580363983512256652839038466984
    2077649278811148299629990130790497978399974693652401690797312244381
    3361707149818144672666187219454104827980338677164658343636350711365
    5439356428629292972296177350244602806380313370817060034433662955746
    8801063578447437644962364569698707634360652047981718378070013667111
    14240420007076730617258541919943310440740965418798778412503676622857
    23041483585524168262220906489642018075101617466780496790573690289968
    37281903592600898879479448409585328515842582885579275203077366912825
    60323387178125067141700354899227346590944200352359771993651057202793
    97605290770725966021179803308812675106786783237939047196728424115618
    157928677948851033162880158208040021697730983590298819190379481318411
    255533968719576999184059961516852696804517766828237866387107905434029
    413462646668428032346940119724892718502248750418536685577487386752440
    668996615388005031531000081241745415306766517246774551964595292186469
    1082459262056433063877940200966638133809015267665311237542082678938909
    1751455877444438095408940282208383549115781784912085789506677971125378
    2833915139500871159286880483175021682924797052577397027048760650064287
    4585371016945309254695820765383405232040578837489482816555438621189665
    7419286156446180413982701248558426914965375890066879843604199271253952
    

    【讨论】:

      【解决方案2】:

      您可以在推送最后一项之前设置条件:

      toPush &lt; num ? fibs.push(toPush) : 0;

      function findFibs(num) {
        
        if (num < 2) {
          return [1,1];
        } else {
          var fibs = findFibs(num - 1)
          if ((fibs[fibs.length - 1]) < num) {
            let toPush = fibs[fibs.length - 1] + fibs[fibs.length - 2];
            toPush < num ? fibs.push(toPush) : 0;
          }
          return fibs;
        }
      }
      
      console.log(findFibs(20));

      【讨论】:

        【解决方案3】:

        输出21的原因是因为ins if语句的条件错误。
        (fibs[fibs.length - 1]) &lt; num

        这意味着最后一个条目是 13 小于 20。 所以 13 加起来 8 并在 fibs 数组中存储为 21。 如果将 if 语句更改为
        if ((fibs[fibs.length - 1]+ fibs[fibs.length - 2]) &lt; num )

        然后你可以期待你需要的输出。

        新代码:

        function findFibs(num) {
          
            if (num < 2) {
              return [1,1];
            } else {
              var fibs = findFibs(num - 1)
              console.log(fibs)
              if ((fibs[fibs.length - 1]+ fibs[fibs.length - 2]) < num ) {
                fibs.push(fibs[fibs.length - 1] + fibs[fibs.length - 2])
              }
              return fibs;
            }
          }
          
          console.log(findFibs(20));
        

        【讨论】:

          【解决方案4】:

          首先分解代码,了解它在做什么。

          function findFibs(num) {
          
              // checking if the given num is less than 2
              if (num < 2) {
                  return [1, 1];
              
              } else { 
                  // decrement the given number by 1, which is 19, 18, 17....
                  // and reenter the loop with it
                  var fibs = findFibs(num - 1)
                  
                  // grab the last and second last numbers
                  const a = fibs[fibs.length - 1];
                  const b = fibs[fibs.length - 2];
          
                  // check if the last number is less than num
                  // AND if the sum of a+b is less than num
                  // see? you should also check for (a + b) < num. this is what you missed.
                  if (a < num && (a + b) < num) {
          
                      // if so, add a new item to the array fibs
                      fibs.push(a + b)
                  }
                  return fibs;
              }
          }
          
          console.log(findFibs(20));
          
          

          查看push method on W3Schools

          顺便说一句,你的最后一行代码console.log(sumFibs(20)); 应该是console.log(findFibs(20));。否则您的代码将无法正常运行。编码愉快!

          【讨论】:

          • 这真的很有帮助,谢谢!是的,我确实需要分解我的代码并了解每一行的作用。
          猜你喜欢
          • 2017-11-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-26
          • 2020-10-18
          • 2014-08-07
          相关资源
          最近更新 更多