【发布时间】:2017-03-05 07:57:16
【问题描述】:
以下问答涵盖了在 Swift 中生成斐波那契数的几种方法,但它已经过时了(Swift 1.2?):
问题:我们如何使用现代 Swift (Swift >= 3) 巧妙地生成斐波那契数?最好是避免显式递归的方法。
【问题讨论】:
标签: swift swift3 sequence fibonacci
以下问答涵盖了在 Swift 中生成斐波那契数的几种方法,但它已经过时了(Swift 1.2?):
问题:我们如何使用现代 Swift (Swift >= 3) 巧妙地生成斐波那契数?最好是避免显式递归的方法。
【问题讨论】:
标签: swift swift3 sequence fibonacci
sequence(state:next:)函数作为替代方案,我们可以使用一个简洁的全局 sequence 函数,这是在 Swift 3.0 中实现的一对函数(如进化提案 SE-0094 中所述)。
使用后者,我们可以在sequence(state:next:) 的next 闭包中将斐波那契数列的先前和当前状态保留为可变的state 属性。
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
})
}
或者,使用 tuple hacks 来压缩它(但是执行 next 一个额外的、不必要的时间)
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 })
}
请注意,当不再满足 ... <= through 条件时,我们会以 nil 返回显式终止序列。
示例用法:
// 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
我们还可以从上面删除终止条件来构造一个无限的斐波那契数序列,以组合使用,例如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 到来时,序列的 prefix(while:) 方法,如进化提案 SE-0045 中所述,将已经实现。使用这个附加功能,我们可以修改上面的fibs 方法来避免显式的 by-nil 条件序列终止:
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 })
}
示例应该与上面的 Swift 3.0 相同。
【讨论】:
for f in sequence(first: (0, 1), while: { $1 <= 50 }, next: { ($1, $0 + $1)}) { print(f.1) } 打印斐波那契数。
Swift 3.0 的替代方法是使用辅助函数
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)
}
来自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
请注意,为了在结果序列中包含零,它
将初始值(0, 1)替换为(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
这使得“人工”检查
if pair.1 == 0 { pair.1 = 1; return 0 }
冗余。根本原因是斐波那契数可以 推广到负指数(https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers):
... -8, 5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, ...
【讨论】:
sequence都可以降格成return sequence(state: first, next: { (condition($0) ? $0 : Optional<T>.none, $0 = next($0)).0 })。
next($0)。
defer(原版)不会被执行(到达)以防guard 中断并返回nil?
defer 语句没有被执行,那么它的代码块根本就没有被调度执行。
在 Swift 3.1 中,这是一个永远生成斐波那契数的迭代器,以及从它派生的无限序列:
class FibIterator : IteratorProtocol {
var (a, b) = (0, 1)
func next() -> Int? {
(a, b) = (b, a + b)
return a
}
}
let fibs = AnySequence{FibIterator()}
要打印前 10 个斐波那契数:
> print(Array(fibs.prefix(10)))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
如果您想过滤或映射这个无限序列,您需要首先调用.lazy,否则过滤器或映射将严格执行并且不会终止。以下是前 5 个偶数斐波那契数:
> print( Array(fibs.lazy.filter{$0 % 2 == 0}.prefix(5)) )
[2, 8, 34, 144, 610]
【讨论】:
使用递归是不好的!递归是邪恶的!
我宁愿这样做:
func fibo(_ n:Int) -> Int {
var a = 0
var b = 1
for _ in 0..<n {
a += b
b = a - b
}
return a
}
这样更快更干净!
【讨论】:
Xcode 9.3.1、Swift 4.1
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)
}
}
let fibonacciSequence = [Int](fibonacci: 15)
let index = 12
print(fibonacciSequence)
print(fibonacciSequence[index])
let value = [Int].calculate(fibonacciAt: index)
print("\(value!)")
【讨论】:
详情
XCode 版本 10.0 beta 6,Swift 4.2
需要控制流来获取从 0 开始的斐波那契序列的第一次或前两次迭代。
时间复杂度:O(n)
空间复杂度:O(n)
代码
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
}
}
用法
fib(8)
//打印(fib(8))
【讨论】:
摘自 David kopec 的《Swift 中的经典计算机科学问题》一书:
递归
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]!
}
通过迭代方法
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
}
【讨论】:
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)
【讨论】:
func fibonacci(iteration: Int) -> Int {
return Int(round(pow(1.618033988749895, Double(iteration)) / 2.23606797749979))
}
那么它是如何工作的:
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]
在 70 次迭代之前完美运行。
308061521170130 而不是 308061521170129
【讨论】:
我刚刚看到 Dhaval Gevariya 代码,只是将 print fibonacci 移到上面而不是下面,现在它也会打印 0
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)
【讨论】:
// MARK: - 功能
func fibonacciSeries(_ num1 : Int,_ num2 : Int,_ term : Int,_ termCount : Int) -> Void{
if termCount != term{
print(num1)
fibonacciSeries(num2, num2+num1, term, termCount + 1)
}
}
// MARK: - 函数调用 斐波那契数列(0, 1, 5, 0)
// MARK: - 输出 0 1 1 2 3
注意只需更改斐波那契数列的 No Of 项。
【讨论】: