Fibonacci numbers generator in Swift 3

萝らか妹 提交于 2019-12-17 20:40:30

问题


The following Q&A covers a few methods of generating Fibonacci numbers in Swift, but it's quite outdated (Swift 1.2?):

  • Sum of Fibonacci term using Functional Swift

Question: How could we generate Fibonacci numbers neatly using modern Swift (Swift >= 3)? Preferably methods avoiding explicit recursion.


回答1:


An alternative for Swift 3.0 would be to use the helper function

public func sequence<T>(first: T, while condition: @escaping (T)-> Bool, next: @escaping (T) -> T) -> UnfoldSequence<T, T> {
    let nextState = { (state: inout T) -> T? in
        // Return `nil` if condition is no longer satisfied:
        guard condition(state) else { return nil }
        // Update current value _after_ returning from this call:
        defer { state = next(state) }
        // Return current value:
        return state
    }
    return sequence(state: first, next: nextState)
}

from Express for loops in swift with dynamic range:

for f in sequence(first: (0, 1), while: { $1 <= 50 }, next: { ($1, $0 + $1)}) {
    print(f.1)
}
// 1 1 2 3 5 8 13 21 34

Note that in order to include zero in the resulting sequence, it suffices to replace the initial value (0, 1) by (1, 0):

for f in sequence(first: (1, 0), while: { $1 <= 50 }, next: { ($1, $0 + $1)}) {
    print(f.1)
}
// 0 1 1 2 3 5 8 13 21 34

That makes the "artificial" check

if pair.1 == 0 { pair.1 = 1; return 0 }

redundant. The underlying reason is that the Fibonacci numbers can be generalized to negative indices (https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers):

 ... -8, 5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, ...



回答2:


Using the global sequence(state:next:) function

Swift 3.0

As one alternative we could make use of one the neat global sequence functions, a pair of functions that were implemented in Swift 3.0 (as described in evolution proposal SE-0094).

  • sequence(first:next:)
  • sequence(state:next:)

Using the latter of these, we may keep the previous and current state of the Fibonacci numbers sequence as the mutable state property in the next closure of sequence(state:next:).

func fibs(through: Int, includingZero useZero: Bool = false)
    -> UnfoldSequence<Int, (Int, Int)> {
    return sequence(state: useZero ? (1, 0) : (0, 1),
                    next: { (pair: inout (Int, Int)) -> Int? in
        guard pair.1 <= through else { return nil }
        defer { pair = (pair.1, pair.0 + pair.1) }
        return pair.1
        })
}
    // explicit type annotation of inout parameter closure
    // needed due to (current) limitation in Swift's type
    // inference

// alternatively, always start from one: drop useZero 
// conditional at 'state' initialization
func fibs1(through: Int)
    -> UnfoldSequence<Int, (Int, Int)> {
    return sequence(state: (0, 1),
                    next: { (pair: inout (Int, Int)) -> Int? in
        guard pair.1 <= through else { return nil }
        defer { pair = (pair.1, pair.0 + pair.1) }
        return pair.1
        })
}

Or, condensing this using tuple hacks (however executing next one extra, unnecessary, time)

func fibs(through: Int, includingZero useZero: Bool = false) -> UnfoldSequence<Int, (Int, Int)> {
    return sequence(state: useZero ? (1, 0) : (0, 1), next: { 
        ($0.1 <= through ? $0.1 : Optional<Int>.none, $0 = ($0.1, $0.0 + $0.1)).0 })
}

func fibs1(through: Int) -> UnfoldSequence<Int, (Int, Int)> {
    return sequence(state: (0, 1), next: { 
        ($0.1 <= through ? $0.1 : Optional<Int>.none, $0 = ($0.1, $0.0 + $0.1)).0 })
}

Note that we explicitly terminate the sequences with a nil return when the ... <= through condition is no longer met.

Example usage:

// fib numbers up through 50, excluding 0
fibs(through: 50).forEach { print($0) }
    // 1 1 2 3 5 8 13 21 34

// ... or
fibs1(through: 50).forEach { print($0) }
    // 1 1 2 3 5 8 13 21 34

// ... including 0
fibs(through: 50, includingZero: true).forEach { print($0) }
    // 0 1 1 2 3 5 8 13 21 34

// project Euler #2: sum of even fib numbers up to 4000000
print(fibs(through: 4_000_000)
    .reduce(0) { $1 % 2 == 0 ? $0 + $1 : $0 }) // 4 613 732

We could also remove the termination criteria from above to construct an infinite sequence of fibonacci numbers, to be used in combination e.g. with prefix:

func infFibs() -> UnfoldSequence<Int, (Int, Int)> {
    return sequence(state: (0, 1), next: {
        (pair: inout (Int, Int)) -> Int in (pair.1, pair = (pair.1, pair.0 + pair.1)).0 })
}

// prefix the first 6 fib numbers (excluding 0) from
// the infinite sequence of fib numbers
infFibs().prefix(10).forEach { print($0) }
    // 1 1 2 3 5 8 13 21 34 55

Swift 3.1

When Swift 3.1 arrives, the prefix(while:) method for sequences, as described in evolution proposal SE-0045, will have been implemented. Using this additional feature, we can modify the fibs methods above to avoid the explicit by-nil conditional sequence termination:

func fibs(through: Int, startingFromZero useZero: Bool = false)
    -> AnySequence<Int> {
    return sequence(state: useZero ? (1, 0) : (0, 1),
                    next: { (pair: inout (Int, Int)) -> Int? in
        defer { pair = (pair.1, pair.0 + pair.1) }
        return pair.1
        }).prefix(while: { $0 <= through })
}

// alternatively, always start from one: drop useZero 
// conditional at 'state' initialization
func fibs1(through: Int) -> AnySequence<Int> {
    return sequence(state: (0, 1),
                    next: { (pair: inout (Int, Int)) -> Int? in
        defer { pair = (pair.1, pair.0 + pair.1) }
        return pair.1
        }).prefix(while: { $0 <= through })
}

Examples should work the same as for Swift 3.0 above.




回答3:


In Swift 3.1, here's an iterator that generates Fibonacci numbers forever, and an infinite sequence derived from it:

class FibIterator : IteratorProtocol {
    var (a, b) = (0, 1)

    func next() -> Int? {
        (a, b) = (b, a + b)
        return a
    }
}

let fibs = AnySequence{FibIterator()}

To print the first 10 Fibonacci numbers:

> print(Array(fibs.prefix(10)))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

If you want to filter or map this infinite sequence you'll need to call .lazy first, since otherwise filter or map will behave strictly and will not terminate. Here are the first 5 even Fibonacci numbers:

> print( Array(fibs.lazy.filter{$0 % 2 == 0}.prefix(5)) )
[2, 8, 34, 144, 610]



回答4:


Details

Xcode 9.3.1, Swift 4.1

Solution

extension Array where Element: BinaryInteger {

    private mutating func fibonacci(index: Int) {
        if index >= count {
            return
        }
        self[index] = self[index-1] + self[index-2]
        return fibonacci(index: index+1)
    }

    init(fibonacci count: Int) {
        self = [Element]()
        if count < 0 {
            self = [Element]()
        }
        self = [Element](repeating: 1, count: count)
        fibonacci(index: 2)
    }

    static func calculate(fibonacciAt index: Int) -> Element? {

        if index < 0 {
            return nil
        }

        if index < 2 {
            return 1
        }

        func calc(a: Element, b: Element, index: Int) -> Element {
            if index == 1 {
                return b
            }
            return calc(a: b, b: a+b, index: index-1)
        }

        return calc(a: 1, b: 1, index: index)
    }
}

Usage

let fibonacciSequence = [Int](fibonacci: 15)
let index = 12
print(fibonacciSequence)
print(fibonacciSequence[index])
let value = [Int].calculate(fibonacciAt: index)
print("\(value!)")

Results




回答5:


Details

XCode Version 10.0 beta 6, Swift 4.2

The control flow is required to get either the first or the first two iterations of the fibonacci seq starting with 0.

Time Complexity: O(n)
Space Complexity: O(n)

Code

func fib(_ n: Int) -> [Int] {

 var fibs: [Int] = [0, 1]
 switch n
 {
 case 1:  return [fibs[0]]
 case 2:  return [fibs[0],fibs[1]]
 default:

 (2...n-1).forEach
 { i in
     fibs.append(fibs[i - 1] + fibs[i - 2])
 }

 return fibs
 }
}

Usage

fib(8)

//print(fib(8))




回答6:


From David kopec's book “Classic Computer Science Problems in Swift”:

By recursion

 var fibMemo: [UInt: UInt] = [0: 0, 1: 1] // our old base cases

 func fib3(n:  UInt) ­> UInt
 {

    if let result = fibMemo[n] 
    { 
       // our new base case

       return result
    } 
    else 
    {

       fibMemo[n] = fib3(n: n ­ 1) + fib3(n: n ­ 2) // memoization
    }

   return fibMemo[n]!
 }

By iterative approach

func fib4(n: UInt) ­> UInt
{

     if (n == 0) 
     {
        // special case

        return n
     }

     var last: UInt = 0, next: UInt = 1 // initially set to fib(0) & fib(1          

     for _ in 1..<n {

          (last, next) = (next, last + next) }

     return next
}



回答7:


func fibonaci(n: Int)
{
    var fiboNumberOne = 1
    var fiboNumberTwo = 0

    for i in 0..<n
    {
        let temp = fiboNumberOne + fiboNumberTwo
        fiboNumberOne = fiboNumberTwo
        fiboNumberTwo = temp
        print("Fibonaci \(fiboNumberTwo)")

    }
}

 fibonaci(n: 5)



回答8:


If you don't need accuracy there is O(1) function for your needs:

func fibonacci(iteration: Int) -> Int {
  return Int(round(pow(1.618033988749895, Double(iteration)) / 2.23606797749979))
}

So here how it works:

print((0..<40).map(fibonacci))
// prints [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]

Works perfectly until 70 iteration.

Warning: On 71 iteration returns 308061521170130 instead of 308061521170129




回答9:


This is bad to use recursion!! recursion is evil!

I would have rather done it this way:

func fibo(_ n:Int) -> Int {

    var a = 0
    var b = 1

    for _ in 0..<n {
        a += b
        b = a - b
    }

    return a
}

Which is much faster and cleaner!



来源:https://stackoverflow.com/questions/40203182/fibonacci-numbers-generator-in-swift-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!