1

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

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

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192

12 Answers12

7

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, ...
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Nice, didn't previously know about the negative indices part! – dfrib Oct 23 '16 at 14:03
  • Actually, it seems the the whole body of the helper `sequence` can be downhacked into `return sequence(state: first, next: { (condition($0) ? $0 : Optional.none, $0 = next($0)).0 })`. – dfrib Jan 01 '17 at 13:59
  • @dfri: No, that would compute `next($0)` even if the condition is not satisfied, i.e. "once too often". – Martin R Jan 01 '17 at 14:03
  • Ah, my bad, the `defer` (in the original) wont be executed (reached) in case the `guard` breaks and returns `nil`? – dfrib Jan 01 '17 at 14:05
  • 1
    @dfri: Exactly. If the `defer` statement is not executed then its code block is not scheduled for execution at all. – Martin R Jan 01 '17 at 14:06
  • I would like to try `return sequence(state: first, next: { condition($0) ? ($0, $0 = next($0)).0 : Optional.none })` but it seems to crash the compiler for me. – dfrib Jan 01 '17 at 14:12
5

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).

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.

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • That reminds me of the helper function suggested in http://stackoverflow.com/a/40070339/1187415, which can be used quite universally. Using that, you can print the fibonacci numbers with `for f in sequence(first: (0, 1), while: { $1 <= 50 }, next: { ($1, $0 + $1)}) { print(f.1) }`. – Martin R Oct 23 '16 at 13:23
  • @MartinR That's neat indeed! I've already previously upvoted the linked answer, but if you have the time and feel up for it, an answer based on that helper would be a great addition to this thread :) – dfrib Oct 23 '16 at 13:47
  • Please excuse me for pinging you like this, but as I think that you are interested in the performance of algorithms in Swift, I would like to invite you to have a look at http://codereview.stackexchange.com/q/158798/35991 and http://codereview.stackexchange.com/q/158799/35991 ! – Martin R Mar 29 '17 at 13:33
  • @MartinR No worries, I'm happy for the ping, thanks (will use this as a chance to take my Knuth collection out of the shelf). Will look at some evening this week, and see if I can come with some constructive advice. Btw, since you also ask for Swiftyness/semantics/etc, you might want to also ping Hamish (if you haven't already), I think he'll be interested in the subject, as well as eager to possibly help out. – dfrib Mar 29 '17 at 14:22
4

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]
Adam Dingle
  • 3,114
  • 1
  • 16
  • 14
2

I have just saw Dhaval Gevariya code and just move print fibonacci above instead below and now it will print 0 also

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

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

 fibonaci(n: 5)
Deepak Ghadi
  • 163
  • 2
  • 5
1

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
}
H S W
  • 6,310
  • 4
  • 25
  • 36
1
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)
Dhaval Gevariya
  • 870
  • 1
  • 12
  • 16
1

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

Dmitry Kozlov
  • 1,115
  • 10
  • 14
0

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

enter image description here

Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
0

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))

lazazael
  • 11
  • 3
0

// MARK: - Function

 func fibonacciSeries(_ num1 : Int,_ num2 : Int,_ term : Int,_ termCount : Int) -> Void{
        if termCount != term{
            print(num1)
            fibonacciSeries(num2, num2+num1, term, termCount + 1)
        }
    }
    

// MARK: - Calling Of Function fibonacciSeries(0, 1, 5, 0)

// MARK: - out Put 0 1 1 2 3

Note Need to Change only No Of term for fibonacci Series.

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

print(fibonacci(n: 10))
jmoerdyk
  • 5,544
  • 7
  • 38
  • 49
-4

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!

  • Recursion is not necessarily evil. See an explanation and discussion here: https://stackoverflow.com/questions/3021/what-is-recursion-and-when-should-i-use-it – Jann Nov 10 '17 at 17:37
  • 1
    None of the other answers use recursion either: they use sequence generators. – dfrib Nov 10 '17 at 18:49