Fibonacci numbers generator in Swift 3
Asked Answered
S

13

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.

Sensational answered 23/10, 2016 at 12:58 Comment(0)
E
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, ...
Erogenous answered 23/10, 2016 at 13:55 Comment(6)
Nice, didn't previously know about the negative indices part!Sensational
Actually, it seems the the whole body of the helper sequence can be downhacked into return sequence(state: first, next: { (condition($0) ? $0 : Optional<T>.none, $0 = next($0)).0 }).Sensational
@dfri: No, that would compute next($0) even if the condition is not satisfied, i.e. "once too often".Erogenous
Ah, my bad, the defer (in the original) wont be executed (reached) in case the guard breaks and returns nil?Sensational
@dfri: Exactly. If the defer statement is not executed then its code block is not scheduled for execution at all.Erogenous
I would like to try return sequence(state: first, next: { condition($0) ? ($0, $0 = next($0)).0 : Optional<T>.none }) but it seems to crash the compiler for me.Sensational
S
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.

Sensational answered 23/10, 2016 at 12:58 Comment(4)
That reminds me of the helper function suggested in https://mcmap.net/q/77212/-express-for-loops-in-swift-with-dynamic-range, 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) }.Erogenous
@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 :)Sensational
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 codereview.stackexchange.com/q/158798/35991 and codereview.stackexchange.com/q/158799/35991 !Erogenous
@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.Sensational
T
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]
Tamatave answered 15/5, 2017 at 18:16 Comment(0)
A
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)
Am‚lie answered 1/3, 2021 at 12:24 Comment(0)
P
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
}
Photomap answered 20/3, 2019 at 16:1 Comment(0)
Q
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)
Quarles answered 7/4, 2019 at 12:58 Comment(0)
O
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

Orwin answered 26/10, 2019 at 0:41 Comment(0)
S
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

Spahi answered 26/5, 2018 at 16:14 Comment(0)
A
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))

Arlindaarline answered 13/9, 2018 at 19:7 Comment(0)
D
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.

Discussion answered 10/9, 2021 at 10:38 Comment(0)
G
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))
Glidewell answered 16/2, 2023 at 18:48 Comment(0)
M
0

Slight improvement of H S W's answer - recusrion generating a list of the fib series, one ingle pure function - performance will be poor - but useful for teaching purposes:

func fibonnaci(numberOfElements n: UInt) -> [Int] {
    let list = [0, 1]
    guard n > 2 else {
        return Array(list.prefix(Int(n)))
    }
    
    func inner(_ l: [Int]) -> [Int] {
        let m = l.count
        guard n != m else { return l }
        return inner(l + [(l[m - 2] + l[m - 1])])
    }
    
    return inner(list)
}

Margit answered 2/11, 2023 at 7:40 Comment(0)
M
-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!

Mahalia answered 10/11, 2017 at 17:32 Comment(2)
Recursion is not necessarily evil. See an explanation and discussion here: #3521Krispin
None of the other answers use recursion either: they use sequence generators.Sensational

© 2022 - 2025 — McMap. All rights reserved.