【问题标题】:why my code is slow when finding Fibonacci sum?为什么我的代码在找到斐波那契和时很慢?
【发布时间】:2016-11-09 12:37:39
【问题描述】:

我正在为project Euler 问题in this repo 写答案 但在我的解决方案中存在一些性能问题

问题 2: 斐波那契数列中的每个新项都是通过添加前两项来生成的。 从 1 和 2 开始,前 10 个术语将是: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... 考虑斐波那契数列中不超过四百万的项,求偶数项之和。

我的解决方案是

func solution2()
{
    func fibonacci(number: Int) -> (Int)
    {
        if number <= 1
        {
            return number
        }
        else
        {
            return fibonacci(number - 1) + fibonacci(number - 2)
        }
    }

    var sum = 0

    print("calculating...")
    for index in 2..<50
    {
        print (index)
        if (fibonacci(index) % 2 == 0)
        {
            sum += fibonacci(index)
        }
    }

    print(sum)
}

我的问题是,为什么在第 42 次迭代后它变得超级慢,我想按照问题所说的那样做 4000000 次,有什么帮助吗?

解决方案 2

func solution2_fast()
{
    var phiOne : Double = (1.0 + sqrt(5.0)) / 2.0
    var phiTwo : Double = (1.0 - sqrt(5.0)) / 2.0

    func findFibonacciNumber (nthNumber : Double) -> Int64
    {
        let nthNumber : Double = (pow(phiOne, nthNumber) - (pow(phiTwo, nthNumber))) / sqrt(5.0)
        return Int64(nthNumber)
    }

    var sum : Int64 = 0

    print("calculating...")
    for index in 2..<4000000
    {
        print (index)
        let f = findFibonacciNumber(Double(index))
        if (f % 2 == 0)
        {
            sum += f
        }
    }

    print(sum)

}

【问题讨论】:

  • 您的代码很慢,而不是 Swift。考虑递归和冗余计算。
  • @DeyaEldeen,您实现它的方式使其呈指数级增长。每个第 N 个数字都在重新计算第 (N-1) 个和第 (N-2) 个数字。并且因此 (N-1)-th 将重新计算 (N-2)-th 和 (N-3) 等。基本上,计算 4 mio 附近的斐波那契的成本也是 4-mio 的函数。
  • 好的,我将标题编辑为[为什么我的代码在查找斐波那契和时很慢?]
  • 观看 WWDC 2014 视频 Advanced Swift Session 404。有很好的解释(和解决方案)
  • 您正在计算前 400 万个斐波那契数的总和。该问题要求所有小于 400 万的斐波那契数的总和。这是非常不同的。不要让问题变得比现在更难。

标签: ios swift xcode macos fibonacci


【解决方案1】:

关于体育问题的最重要的事情是思考它在问什么。

这并不是要求您生成所有小于 4000000 的斐波那契数 F(n)。它要求的是所有小于 4000000 的偶数 F(n) 的总和。

考虑所有 F(n) 的总和,其中 F(n)

1 + 2 + 3 + 5 + 8

我可以通过计算 F(1)、F(2)、F(3) 等等来做到这一点……然后在将它们相加之前检查它们是否小于 10。

或者我可以存储两个变量...

F1 = 1
F2 = 2

总共...

Total = 3

现在我可以把它变成一个 while 循环并完全失去递归。事实上,我正在做的最复杂的事情就是将两个数字相加......

我想出了这个...

func sumEvenFibonacci(lessThan limit: Int) -> Int {
    // store the first two Fibonacci numbers
    var n1 = 1
    var n2 = 2

    // and a cumulative total
    var total = 0

    // repeat until you hit the limit
    while n2 < limit {
        // if the current Fibonacci is even then add to total
        if n2 % 2 == 0 {
            total += n2
        }

        // move the stored Fibonacci numbers up by one.
        let temp = n2
        n2 = n2 + n1
        n1 = temp
    }

    return total
}

它在几分之一秒内运行。

sumEvenFibonacci(lessThan: 4000000)

找到正确答案。

事实上这...sumEvenFibonacci(lessThan: 1000000000000000000) 运行大约半秒。

【讨论】:

    【解决方案2】:

    虽然 Int64 不足以存储结果,但第二种解决方案似乎更快(呃)。 2..91 的斐波那契数之和为 7,527,100,471,027,205,936,但您可以存储在 Int64 中的最大数为 9,223,372,036,854,775,807。为此,您需要使用一些其他类型,例如 BigInteger

    【讨论】:

    • 还有内置的NSDecimalNumber
    • 另外,存在问题被 OP 错误解释的问题。该问题要求所有斐波那契数的总和不超过 4,000,000。它不要求前 4,000,000 个斐波那契数的总和。这导致了一个非常不同的答案。
    【解决方案3】:

    因为你使用递归,它缓存在内存中。如果你迭代42,它可能在你的内存中有很多斐波那契函数,并且是递归的。所以它不适合递归,你可以存储结果在数组中,不是swift的原因。

    【讨论】:

      【解决方案4】:

      这是两种不同的答案

      func solution2_recursive()
      {
          func fibonacci(number: Int) -> (Int)
          {
              if number <= 1
              {
                  return number
              }
              else
              {
                  return fibonacci(number - 1) + fibonacci(number - 2)
              }
          }
          var sum = 0
          print("calculating...")
          for index in 2..<50
          {
              print (index)
              let f = fibonacci(index)
      
              if( f < 4000000)
              {
                  if (f % 2 == 0)
                  {
                      sum += f
                  }
              }
              else
              {
                  print(sum)
                  return
              }
          }
      }
      

      解决方案 2

      func solution2()
      {
          var phiOne : Double = (1.0 + sqrt(5.0)) / 2.0
          var phiTwo : Double = (1.0 - sqrt(5.0)) / 2.0
      
          func findFibonacciNumber (nthNumber : Double) -> Int64
          {
              let nthNumber : Double = (pow(phiOne, nthNumber) - (pow(phiTwo, nthNumber))) / sqrt(5.0)
              return Int64(nthNumber)
          }
      
          var sum : Int64 = 0
      
          print("calculating...")
          for index in 2..<50
          {
              let f = findFibonacciNumber(Double(index))
              if(f < 4000000)
              {
                  if (f % 2 == 0)
                  {
                      sum += f
                  }
              }
              else
              {
                  print(sum)
                  return
              }
          }
      }
      

      【讨论】:

      • 请不要将解决方案放在您的答案中。当人们不能去谷歌搜索解决方案时,欧拉项目会更好。保留代码。它可能有用,但删除解决方案。
      猜你喜欢
      • 2012-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-22
      • 1970-01-01
      相关资源
      最近更新 更多