Save 37% off PRO during our Black Friday Sale! »

用 Swift 學習資工系的經典程式問題 1 遞迴(recursive) & 迭代 (iterative)

用 Swift 學習資工系的經典程式問題 1 遞迴(recursive) & 迭代 (iterative)

遞迴(recursive)的概念。
多種費波那西(Fibonacci)數列的 recursive 解法和優缺點。
recursive & iterative。
畫畫有趣的謝爾賓斯基三角形 (Sierpinski triangle ) &碎形。
從 Xcode 研究 stack frame。
其它例子: 階乘(factorial)計算,pi 的計算(Leibniz),河內塔(the tower of haoni) 。

Transcript

  1. ⽤用 Swift 學習資⼯工系的經典程式 問題 1 遞迴(recursive) & 迭代 (iterative)

  2. 彼得潘 http://apppeterpan.strikingly.com

  3. 叫我彼得潘,Peter,Deeplove, ⿁鬼塚,Swift⼩小王⼦子,情歌王⼦子 莫叫我老師 http://bit.ly/2xe9eOG

  4. ⼀一輩⼦子的朋友 https://www.youtube.com/watch?v=VY3VvPx9rYU

  5. 問題 ? All paths through this function will call itself

  6. 問題 ? func 最想留留住的幸運() { print("原來來彼得潘是我最想留留住的幸運") print("原來來我們和愛情曾經靠得那麼近") 最想留留住的幸運() } 最想留留住的幸運()

    func 最想留留住的幸運() { print("原來來彼得潘是我最想留留住的幸運") print("原來來我們和愛情曾經靠得那麼近") 最想留留住的幸運() } 最想留留住的幸運() func 最想留留住的幸運() { print("原來來彼得潘是我最想留留住的幸運") print("原來來我們和愛情曾經靠得那麼近") 最想留留住的幸運() } 最想留留住的幸運()
  7. recursive function func 最想留留住的幸運() { print("原來來彼得潘是我最想留留住的幸運") print("原來來我們和愛情曾經靠得那麼近") 最想留留住的幸運() } 最想留留住的幸運()

    在 function 裡呼叫⾃自⼰己的 function 問題: ⼀一直呼叫,永不結束
  8. recursive function 需要有結束的 條件 var names = ["彼得潘", "⼩小王⼦子", "虎克",

    "溫蒂"] func 最想留留住的幸運(names: [String], index: Int) { if index < names.count { let name = names[index] if name != "虎克" { print("原來來\(name)是我最想留留住的幸運") print("原來來我們和愛情曾經靠得那麼近") 最想留留住的幸運(names: names, index: index + 1) } } } 最想留留住的幸運(names: names, index: 0)
  9. recursive function 需要有結束的 條件 var names = ["彼得潘", "⼩小王⼦子", "虎克",

    "溫蒂"] func 最想留留住的幸運(names: [String], index: Int) { let space = String(repeating: " ", count: index) print("\(space)開始\(#function) index:\(index)") if index < names.count { let name = names[index] if name != "虎克" { print("\(space)原來來\(name)是我最想留留住的幸運") print("\(space)原來來我們和愛情曾經靠得那麼近") 最想留留住的幸運(names: names, index: index + 1) } } print("\(space)結束\(#function) index:\(index)") } 最想留留住的幸運(names: names, index: 0)
  10. None
  11. 從中斷點查 stack Continue program execution 讓 App 繼續執⾏行行 index 的內容

  12. stack frame 當 function 呼叫時,會產⽣生⼀一個 frame,加到 stack 裡

  13. stack frame 從左邊列列表的 frame 順序可看出 function 呼叫的順序 frame 順序是由新到舊, frame

    0是最新最後⼀一個執⾏行行的 function frame 1 的 function 裡呼叫了了 frame 0 的 function 可從 stack frame 查 App 閃退最後執⾏行行的程式
  14. 切換 stack frame 從左邊列列表點選切換

  15. step over: 執⾏行行下⼀一⾏行行程式 當 function 執⾏行行結束時,frame 會從 stack 移除 此時左邊的

    frame 列列表會少⼀一個 frame
  16. App 裡執⾏行行永不結束的 recursive function func 最想留留住的幸運() { print("原來來彼得潘是我最想留留住的幸運") print("原來來我們和愛情曾經靠得那麼近") 最想留留住的幸運()

    } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. 最想留留住的幸運() }
  17. Infinite recursion stack overflow: 太多 frame 了了 ! App 閃退

    !
  18. 何時要⽤用 recursive function 有⼀一定的規則

  19. Fibonacci sequence 費波那⻄西數列列 0, 1, 1, 2, 3, 5, 8,

    13, 21... 規則是 ? https://www.mathsisfun.com/numbers/fibonacci-sequence.html
  20. Fibonacci sequence 費波那⻄西數列列 0, 1, 1, 2, 3, 5, 8,

    13, 21... 數字是前兩兩個數字加起來來 ( 從第三個數字開始)
  21. Fibonacci sequence 第⼀一版 func fibonacciSequence(index: Int) -> Int { return

    fibonacciSequence(index: index - 1) + fibonacciSequence(index: index - 2) } fibonacciSequence(index: 3)
  22. 修改第⼀一版練習

  23. Fibonacci sequence 第⼆二版 func fibonacciSequence(index: Int) -> Int { if

    index < 2 { return index } else { return fibonacciSequence(index: index - 1) + fibonacciSequence(index: index - 2) } } fibonacciSequence(index: 3)
  24. fibonacciSequence 呼叫了了幾次 ?

  25. fibonacciSequence 呼叫了了 5 次

  26. fibonacciSequence 呼叫了了 5 次 fibonacciSequence(index: 3) fibonacciSequence(index: 2) fibonacciSequence(index: 1)

    1 fibonacciSequence(index: 1) fibonacciSequence(index: 0) 0 1
  27. fibonacciSequence 呼叫了了 89 + 88 次 1 有更更快的⽅方法嗎 ?

  28. 修改第⼆二版練習

  29. fibonacciSequence(index: 4) fibonacciSequence(index: 3) fibonacciSequence(index: 2) fibonacciSequence(index: 2) fibonacciSequence(index: 1)

    將算過的存起來來,不⽤用再算⼀一次 Memorization 記憶
  30. Fibonacci sequence 第三版 var fiboDic = [0: 0, 1: 1]

    func fibonacciSequence(index: Int) -> Int { if let number = fiboDic[index] { return number } else { fiboDic[index] = fibonacciSequence(index: index - 1) + fibonacciSequence(index: index - 2) return fiboDic[index]! } } fibonacciSequence(index: 10)
  31. 程式範例例 https://github.com/AppPeterPan/Recursive2

  32. fibonacciSequence 呼叫了了 10 + 9 次 10 + 9 <

    89 + 88 有更更快的⽅方法嗎 ?
  33. 比第三版更更快的⽅方法練習

  34. Fibonacci sequence 第四版 iterative: 利利⽤用迴圈 func fibonacciSequence(index: Int) -> Int

    { if index < 2 { return index } else { var firstNumber = 0 var secondNumber = 1 for _ in 2...index { let newNumber = firstNumber + secondNumber firstNumber = secondNumber secondNumber = newNumber } return secondNumber } } fibonacciSequence(index: 10)
  35. fibonacciSequence 呼叫了了 9 次 不宣告 newNumber 的⽅方法 ?

  36. 利利⽤用 tuple func fibonacciSequence(index: Int) -> Int { if index

    < 2 { return index } else { var firstNumber = 0 var secondNumber = 1 for _ in 2...index { (firstNumber, secondNumber) = (secondNumber, firstNumber + secondNumber) } return secondNumber } } https://bit.ly/2zNt2uO
  37. Iteration & Recursion • Recursion 的好: • 程式可能比較精簡 • 想到規則後,可能比較好寫

    • Recursion 的壞: • 比較難懂 • 可能比較慢 • 可能⽤用比較多記憶體
  38. 九九乘法 Iteration for i in 1...9 { for j in

    1...9 { print("\(i) * \(j) = \(i * j)") } }
  39. 九九乘法 Recursion 練習

  40. 九九乘法 Recursion func timesTable(number1: Int, number2: Int) { print("\(number1) *

    \(number2) = \(number1 * number2)") if number2 < 9 { timesTable(number1: number1, number2: number2 + 1) } else if number1 < 9 { timesTable(number1: number1 + 1, number2: 1) } } timesTable(number1: 1, number2: 1) 規則: 兩兩個數字相乘
  41. 河內塔 the tower of haoni ⼩小的盤⼦子要放在⼤大的盤⼦子上 ⽬目標: 將 A 的盤⼦子搬到

    C 上 http://www.chiuchang.com.tw/toy/hanoi/hanoi.html Recursive 難在找規則 剛剛的 Fibonacci sequence 規則很明顯
  42. 河內塔的圓盤不太好想像,考慮 請 4 位同學上台當圓盤,以⾝身⾼高 為⼤大⼩小,⽬目標找⾝身⾼高 150, 160, 170, 180 的

    4 個⼈人 Demo
  43. ⼀一個盤⼦子 ? A B C A B C 1 2

    有 A, B, C 三個塔,將 A 的盤⼦子搬到 C
  44. 兩兩個盤⼦子 ? A B C A B C 1 2

    將 A 上比較⼩小的盤⼦子移到 B,才能將⼤大的盤⼦子到移到 C
  45. 兩兩個盤⼦子 ? A B C A B C 2 3

  46. 兩兩個盤⼦子 ? A B C A B C 3 4

    剩⼀一個盤⼦子,直接搬
  47. ⼯工具⼈人的故事 溫蒂想將兩兩個禮物(⼀一⼤大⼀一⼩小)送給彼得潘 但是她⼀一次只能送⼀一個, ⽽而且彼得潘希望先收到⼤大的,再收到⼩小的 (因為先收到⼩小的就不會理理溫蒂了了) 溫蒂先將⼩小的禮物丟給⼯工具⼈人虎克, 然後將⼤大的禮物送給彼得潘 然後再請虎克將⼩小的禮物寄給彼得潘 A(溫蒂) B(虎克)

    C(彼得潘)
  48. 三個盤⼦子 ? A B C A B C 將 A

    上比較⼩小的兩兩個盤⼦子移到 B,才能將最⼤大的盤⼦子到移到 C (可以先想像 A 上最⼤大的盤⼦子不存在, 參參考兩兩個盤⼦子的步驟將 A 的兩兩個盤⼦子移到 B) 1 2 B ⼜又要當⼯工具⼈人了了 !
  49. 三個盤⼦子 ? A B C A B C 2 3

  50. 三個盤⼦子 ? A B C A B C 已完成將 A

    之上的兩兩個盤⼦子移到 B (參參考兩兩個盤⼦子的步驟) 3 4
  51. 三個盤⼦子 ? A B C A B C 接下來來將 B

    之上的兩兩個盤⼦子移到 C (可以當成 C 上的盤⼦子不存在,參參考兩兩個盤⼦子的步驟) 4 5
  52. 四個盤⼦子 ? A B C 將 A 上比較⼩小的三個盤⼦子移到 B,才能將最⼤大的盤⼦子到移到 C

    (可以先想像 A 上最⼤大的盤⼦子不存在, 參參考三個盤⼦子的步驟將 A 的三個盤⼦子移到 B)
  53. 規則 有 A, B, C 三個塔,將 A 的盤⼦子搬到 C •

    1 個盤⼦子 • 直接搬 • 2 個盤⼦子 • 將 A 上比較⼩小的 1(2-1)個盤⼦子移到 B,然後再將最⼤大的盤⼦子到移到 C。接下來來再 將 B 上的 1 盤⼦子移到 C。 • 3 個盤⼦子 • 將 A 上比較⼩小的 2(3-1)個盤⼦子移到 B,然後再將最⼤大的盤⼦子到移到 C。接下來來再 將 B 上的 2 盤⼦子移到 C。 • 4 個盤⼦子 • 將 A 上比較⼩小的 3(4-1)個盤⼦子移到 B,然後再將最⼤大的盤⼦子到移到 C。接下來來再 將 B 上的 3 盤⼦子移到 C。
  54. 規則 • 當 tower上有 n ( > 1) 個盤⼦子時 •

    將 tower 上比較⼩小的 (n-1) 個盤⼦子移到輔助的 tower。 (⼯工具⼈人) • 將 tower 上最⼤大的盤⼦子到移到⽬目標的 tower • 將輔助的 tower 的盤⼦子移到⽬目標的 tower。 • 當剩下⼀一個盤⼦子時,可以直接移到⽬目標的 tower。
  55. Demo

  56. 碎形 https://openhome.cc/Gossip/OpenSCAD/Fractal.html

  57. 畫個同⼼心圓吧 規則: 每⼀一圈的半徑都是外⾯面⼀一圈半徑的⼀一半

  58. 畫個同⼼心圓吧 import UIKit class ConcentricCirclesView: UIView { func drawCircle(in rect:

    CGRect) { if rect.width > 20 { let path = UIBezierPath(roundedRect: rect, cornerRadius: rect.width / 2) UIColor.orange.setStroke() path.stroke() let width = rect.width / 2 let x = self.bounds.midX - width / 2 drawCircle(in: CGRect(x: x, y: x, width: width, height: width)) } } override func draw(_ rect: CGRect) { drawCircle(in: rect) } } let concentricCirclesView = ConcentricCirclesView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
  59. 運⽤用 UIBezierPath 繪製各種形狀狀 https://bit.ly/2qOJEhF

  60. Sierpinski 三⾓角形 http://www.matrix67.com/blog/archives/280 https://zh.wikipedia.org/wiki/謝爾賓斯基三⾓角形

  61. Sierpinski 三⾓角形

  62. 規則 1 任意取平⾯面上三點A,B,C,組成⼀一三⾓角形 2 任意取三⾓角形ABC內的⼀一點P,畫出 該點 3 畫出 P和三⾓角形其中⼀一個頂點的中點 4

    重複1
  63. Demo 0,0 0, 300 300, 300

  64. Demo 0,0 0, 300 300, 300 300 150

  65. 計算 CGPoint 兩兩點距離的 hypotf & hypot https://bit.ly/2K4OmAs

  66. 如何練習 recursion 練習,練習,練習 • 階層( factorial) • ex: 3! =

    1 * 2 * 3 = 6 • Digits: 從數字產⽣生包含每⼀一位數字的 array • ex: 1234 -> [1, 2, 3, 4] • Power:計算次⽅方 • ex: 5 3 • Euclidian algorithm: 兩兩個整數的最⼤大公因數 • 9 & 6 > 3 • Binary Search • ex: binarySearch(2, [1, 2, 4, 5 ,7, 9]) > true https://www.weheartswift.com/recursion/