├── .gitignore ├── Classic Computer Science Problems in Swift.playground ├── Pages │ ├── Chapter 1.xcplaygroundpage │ │ └── Contents.swift │ ├── Chapter 2.xcplaygroundpage │ │ └── Contents.swift │ ├── Chapter 3.xcplaygroundpage │ │ └── Contents.swift │ ├── Chapter 4.xcplaygroundpage │ │ └── Contents.swift │ ├── Chapter 5.xcplaygroundpage │ │ └── Contents.swift │ ├── Chapter 6.xcplaygroundpage │ │ └── Contents.swift │ ├── Chapter 7.xcplaygroundpage │ │ └── Contents.swift │ └── Chapter 8.xcplaygroundpage │ │ └── Contents.swift ├── Resources │ ├── iris.csv │ └── wine.csv ├── Sources │ └── SwiftPriorityQueue.swift └── contents.xcplayground ├── LICENSE ├── README.md └── cover.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Pages/Chapter 1.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | // Classic Computer Science Problems in Swift Chapter 1 Source 4 | 5 | // Copyright 2017 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | /// Fibonacci 22 | 23 | // uh oh infinite recursion 24 | func fib1(n: UInt) -> UInt { 25 | return fib1(n: n - 1) + fib1(n: n - 2) 26 | } 27 | 28 | // it works, but it's slow 29 | func fib2(n: UInt) -> UInt { 30 | if (n < 2) { // base cases 31 | return n 32 | } 33 | return fib2(n: n - 2) + fib2(n: n - 1) // recursive cases 34 | } 35 | 36 | fib2(n: 2) 37 | fib2(n: 5) 38 | fib2(n: 10) 39 | 40 | // memoization for the win 41 | var fibMemo: [UInt: UInt] = [0: 0, 1: 1] // our old base cases 42 | func fib3(n: UInt) -> UInt { 43 | if let result = fibMemo[n] { // our new base case 44 | return result 45 | } else { 46 | fibMemo[n] = fib3(n: n - 1) + fib3(n: n - 2) // memoization 47 | } 48 | return fibMemo[n]! 49 | } 50 | 51 | fib3(n: 2) 52 | fib3(n: 4) 53 | fib3(n: 10) 54 | fib3(n: 20) 55 | fib3(n: 21) 56 | //print(fibMemo) 57 | 58 | func fib4(n: UInt) -> UInt { 59 | if (n == 0) { // special case 60 | return n 61 | } 62 | var last: UInt = 0, next: UInt = 1 // initially set to fib(0) & fib(1) 63 | for _ in 1.. String { 107 | var gene: String = "" 108 | for index in 0.. OTPKey { 137 | var randomKey: OTPKey = OTPKey() 138 | for _ in 0.. OTPKeyPair { 146 | let dummy = randomOTPKey(length: original.utf8.count) 147 | let encrypted: OTPKey = dummy.enumerated().map { i, e in 148 | return e ^ original.utf8[original.utf8.index(original.utf8.startIndex, offsetBy: i)] 149 | } 150 | return (dummy, encrypted) 151 | } 152 | 153 | func decryptOTP(keyPair: OTPKeyPair) -> String? { 154 | let decrypted: OTPKey = keyPair.key1.enumerated().map { i, e in 155 | e ^ keyPair.key2[i] 156 | } 157 | return String(bytes: decrypted, encoding:String.Encoding.utf8) 158 | } 159 | 160 | decryptOTP(keyPair: encryptOTP(original: "¡Vamos Swift!👍🏼")) 161 | 162 | /// Calculating Pi 163 | 164 | func calculatePi(nTerms: UInt) -> Double { 165 | let numerator: Double = 4 166 | var denominator: Double = 1 167 | var operation: Double = -1 168 | var pi: Double = 0 169 | for _ in 0..: CustomStringConvertible { 184 | private var container: [T] = [T]() 185 | public func push(_ thing: T) { container.append(thing) } 186 | public func pop() -> T { return container.removeLast() } 187 | public var description: String { return container.description } 188 | } 189 | 190 | var numDiscs = 3 191 | var towerA = Stack() 192 | var towerB = Stack() 193 | var towerC = Stack() 194 | for i in 1...numDiscs { // initialize the first tower 195 | towerA.push(i) 196 | } 197 | 198 | towerA 199 | 200 | func hanoi(from: Stack, to: Stack, temp: Stack, n: Int) { 201 | if n == 1 { // base case 202 | to.push(from.pop()) // move 1 disk 203 | } else { // recursive case 204 | hanoi(from: from, to: temp, temp: to, n: n-1) 205 | hanoi(from: from, to: to, temp: temp, n: 1) 206 | hanoi(from: temp, to: to, temp: from, n: n-1) 207 | } 208 | } 209 | 210 | hanoi(from: towerA, to: towerC, temp: towerB, n: numDiscs) 211 | print(towerA) 212 | print(towerB) 213 | print(towerC) 214 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Pages/Chapter 2.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | // Classic Computer Science Problems in Swift Chapter 2 Source 4 | 5 | // Copyright 2017 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | enum Nucleotide: Character, Comparable { 22 | case A = "A", C = "C", G = "G", T = "T" 23 | } 24 | 25 | func <(lhs: Nucleotide, rhs: Nucleotide) -> Bool { 26 | return lhs.rawValue < rhs.rawValue 27 | } 28 | 29 | typealias Codon = (Nucleotide, Nucleotide, Nucleotide) 30 | typealias Gene = [Codon] 31 | 32 | let n1 = Nucleotide.A 33 | let n2 = Nucleotide.C 34 | let n3 = Nucleotide.A 35 | n1 == n2 36 | n1 == n3 37 | n1 < n3 38 | n1 < n2 39 | n2 > n1 40 | n2 < n1 41 | n1 >= n2 42 | 43 | let ac1: Codon = (.A, .T, .G) 44 | let ac2: Codon = (.A, .C, .G) 45 | let ac3: Codon = (.A, .T, .G) 46 | ac1 == ac2 47 | ac1 == ac3 48 | 49 | let geneSequence = "ACGTGGCTCTCTAACGTACGTACGTACGGGGTTTATATATACCCTAGGACTCCCTTT" 50 | 51 | func stringToGene(_ s: String) -> Gene { 52 | var gene = Gene() 53 | for i in stride(from: 0, to: s.count, by: 3) { 54 | guard (i + 2) < s.count else { return gene } 55 | if let n1 = Nucleotide.init(rawValue: s[s.index(s.startIndex, offsetBy: i)]), let n2 = Nucleotide.init(rawValue: s[s.index(s.startIndex, offsetBy: i + 1)]), let n3 = Nucleotide.init(rawValue: s[s.index(s.startIndex, offsetBy: i + 2)]) { 56 | gene.append((n1, n2, n3)) 57 | } 58 | } 59 | return gene 60 | } 61 | 62 | var gene = stringToGene(geneSequence) 63 | 64 | func linearContains(_ array: Gene, item: Codon) -> Bool { 65 | for element in gene where item == element { 66 | return true 67 | } 68 | return false 69 | } 70 | 71 | let acg: Codon = (.A, .C, .G) 72 | linearContains(gene, item: acg) 73 | 74 | func binaryContains(_ array: Gene, item: Codon) -> Bool { 75 | var low = 0 76 | var high = array.count - 1 77 | while low <= high { 78 | let mid = (low + high) / 2 79 | if array[mid] < item { 80 | low = mid + 1 81 | } else if array[mid] > item { 82 | high = mid - 1 83 | } else { 84 | return true 85 | } 86 | } 87 | return false 88 | } 89 | 90 | let sortedGene = gene.sorted(by: <) 91 | binaryContains(sortedGene, item: acg) 92 | 93 | /*let startTime = CFAbsoluteTimeGetCurrent() 94 | for _ in 0..<1000 { 95 | linearContains(gene, item: acg) 96 | } 97 | let elapsedTime = CFAbsoluteTimeGetCurrent() - startTime 98 | 99 | let startTime2 = CFAbsoluteTimeGetCurrent() 100 | for _ in 0..<1000 { 101 | binaryContains(sortedGene, item: acg) 102 | } 103 | let elapsedTime2 = CFAbsoluteTimeGetCurrent() - startTime2*/ 104 | 105 | /// Generic Versions 106 | 107 | func linearContains(_ array: [T], item: T) -> Bool { 108 | for element in array where item == element { 109 | return true 110 | } 111 | return false 112 | } 113 | 114 | func binaryContains(_ array: [T], item: T) -> Bool { 115 | var low = 0 116 | var high = array.count - 1 117 | while low <= high { 118 | let mid = (low + high) / 2 119 | if array[mid] < item { 120 | low = mid + 1 121 | } else if array[mid] > item { 122 | high = mid - 1 123 | } else { 124 | return true 125 | } 126 | } 127 | return false 128 | } 129 | 130 | linearContains([1,5,15,15,15,15,15], item: 5) 131 | binaryContains(["a", "d", "e", "f", "g"], item: "f") 132 | 133 | ///Generating a Maze 134 | 135 | // A Cell represents the status of a grid location in the maze 136 | enum Cell: Character { 137 | case Empty = "O" 138 | case Blocked = "X" 139 | case Start = "S" 140 | case Goal = "G" 141 | case Path = "P" 142 | } 143 | 144 | typealias Maze = [[Cell]] 145 | srand48(time(nil)) // seed random number generator 146 | 147 | // sparseness is the approximate percentage of walls represented 148 | // as a number between 0 and 1 149 | func generateMaze(rows: Int, columns: Int, sparseness: Double) -> Maze { 150 | // initialize maze full of empty spaces 151 | var maze: Maze = Maze(repeating: [Cell](repeating: .Empty, count: columns), count: rows) 152 | // put walls in 153 | for row in 0.. Bool { 179 | return lhs.row == rhs.row && lhs.col == rhs.col 180 | } 181 | 182 | let goal = MazeLocation(row: 9, col: 9) 183 | func goalTest(ml: MazeLocation) -> Bool { 184 | return ml == goal 185 | } 186 | 187 | func successorsForMaze(_ maze: Maze) -> (MazeLocation) -> [MazeLocation] { 188 | func successors(ml: MazeLocation) -> [MazeLocation] { //no diagonals 189 | var newMLs: [MazeLocation] = [MazeLocation]() 190 | if (ml.row + 1 < maze.count) && (maze[ml.row + 1][ml.col] != .Blocked) { 191 | newMLs.append(MazeLocation(row: ml.row + 1, col: ml.col)) 192 | } 193 | if (ml.row - 1 >= 0) && (maze[ml.row - 1][ml.col] != .Blocked) { 194 | newMLs.append(MazeLocation(row: ml.row - 1, col: ml.col)) 195 | } 196 | if (ml.col + 1 < maze[0].count) && (maze[ml.row][ml.col + 1] != .Blocked) { 197 | newMLs.append(MazeLocation(row: ml.row, col: ml.col + 1)) 198 | } 199 | if (ml.col - 1 >= 0) && (maze[ml.row][ml.col - 1] != .Blocked) { 200 | newMLs.append(MazeLocation(row: ml.row, col: ml.col - 1)) 201 | } 202 | 203 | return newMLs 204 | } 205 | return successors 206 | } 207 | 208 | ///Stack 209 | public class Stack { 210 | private var container: [T] = [T]() 211 | public var isEmpty: Bool { return container.isEmpty } 212 | public func push(_ thing: T) { container.append(thing) } 213 | public func pop() -> T { return container.removeLast() } 214 | } 215 | 216 | ///node 217 | class Node: Comparable, Hashable { 218 | let state: T 219 | let parent: Node? 220 | let cost: Float 221 | let heuristic: Float 222 | init(state: T, parent: Node?, cost: Float = 0.0, heuristic: Float = 0.0) { 223 | self.state = state 224 | self.parent = parent 225 | self.cost = cost 226 | self.heuristic = heuristic 227 | } 228 | 229 | var hashValue: Int { return Int(cost + heuristic) } 230 | } 231 | 232 | func < (lhs: Node, rhs: Node) -> Bool { 233 | return (lhs.cost + lhs.heuristic) < (rhs.cost + rhs.heuristic) 234 | } 235 | 236 | func == (lhs: Node, rhs: Node) -> Bool { 237 | return lhs === rhs 238 | } 239 | 240 | 241 | ///dfs 242 | //returns a node containing the goal state 243 | func dfs(initialState: StateType, goalTestFn: (StateType) -> Bool, successorFn: (StateType) -> [StateType]) -> Node? { 244 | // frontier is where we've yet to go 245 | let frontier: Stack> = Stack>() 246 | frontier.push(Node(state: initialState, parent: nil)) 247 | // explored is where we've been 248 | var explored: Set = Set() 249 | explored.insert(initialState) 250 | 251 | // keep going while there is more to explore 252 | while !frontier.isEmpty { 253 | let currentNode = frontier.pop() 254 | let currentState = currentNode.state 255 | // if we found the goal, we're done 256 | if goalTestFn(currentState) { return currentNode } 257 | // check where we can go next and haven't explored 258 | for child in successorFn(currentState) where !explored.contains(child) { 259 | explored.insert(child) 260 | frontier.push(Node(state: child, parent: currentNode)) 261 | } 262 | } 263 | return nil // never found the goal 264 | } 265 | 266 | func nodeToPath(_ node: Node) -> [StateType] { 267 | var path: [StateType] = [node.state] 268 | var node = node // local modifiable copy of reference 269 | // work backwards from end to front 270 | while let currentNode = node.parent { 271 | path.insert(currentNode.state, at: 0) 272 | node = currentNode 273 | } 274 | return path 275 | } 276 | 277 | func markMaze(_ maze: inout Maze, path: [MazeLocation], start: MazeLocation, goal: MazeLocation) { 278 | for ml in path { 279 | maze[ml.row][ml.col] = .Path 280 | } 281 | maze[start.row][start.col] = .Start 282 | maze[goal.row][goal.col] = .Goal 283 | } 284 | 285 | let start = MazeLocation(row: 0, col: 0) 286 | 287 | if let solution = dfs(initialState: start, goalTestFn: goalTest, successorFn: successorsForMaze(maze)) { 288 | let path = nodeToPath(solution) 289 | markMaze(&maze, path: path, start: start, goal: goal) 290 | printMaze(maze) 291 | } 292 | 293 | ///bfs 294 | public class Queue { 295 | private var container: [T] = [T]() 296 | public var isEmpty: Bool { return container.isEmpty } 297 | public func push(_ thing: T) { container.append(thing) } 298 | public func pop() -> T { return container.removeFirst() } 299 | } 300 | 301 | //returns a node containing the goal state 302 | func bfs(initialState: StateType, goalTestFn: (StateType) -> Bool, successorFn: (StateType) -> [StateType]) -> Node? { 303 | // frontier is where we've yet to go 304 | let frontier: Queue> = Queue>() 305 | frontier.push(Node(state: initialState, parent: nil)) 306 | // explored is where we've been 307 | var explored: Set = Set() 308 | explored.insert(initialState) 309 | // keep going while there is more to explore 310 | while !frontier.isEmpty { 311 | let currentNode = frontier.pop() 312 | let currentState = currentNode.state 313 | // if we found the goal, we're done 314 | if goalTestFn(currentState) { return currentNode } 315 | // check where we can go next and haven't explored 316 | for child in successorFn(currentState) where !explored.contains(child) { 317 | explored.insert(child) 318 | frontier.push(Node(state: child, parent: currentNode)) 319 | } 320 | } 321 | return nil // never found the goal 322 | } 323 | 324 | var maze2 = generateMaze(rows: 10, columns: 10, sparseness: 0.2) 325 | if let solution = bfs(initialState: start, goalTestFn: goalTest, successorFn: successorsForMaze(maze2)) { 326 | let path = nodeToPath(solution) 327 | markMaze(&maze2, path: path, start: start, goal: goal) 328 | printMaze(maze2) 329 | } 330 | 331 | //Heuristics 332 | 333 | func euclideanDistance(ml: MazeLocation) -> Float { 334 | let xdist = ml.col - goal.col 335 | let ydist = ml.row - goal.row 336 | return sqrt(Float((xdist * xdist) + (ydist * ydist))) 337 | } 338 | 339 | func manhattanDistance(ml: MazeLocation) -> Float { 340 | let xdist = abs(ml.col - goal.col) 341 | let ydist = abs(ml.row - goal.row) 342 | return Float(xdist + ydist) 343 | } 344 | 345 | //a* 346 | //returns a node containing the goal state 347 | func astar(initialState: StateType, goalTestFn: (StateType) -> Bool, successorFn: (StateType) -> [StateType], heuristicFn: (StateType) -> Float) -> Node? { 348 | // frontier is where we've yet to go 349 | var frontier: PriorityQueue> = PriorityQueue>(ascending: true, startingValues: [Node(state: initialState, parent: nil, cost: 0, heuristic: heuristicFn(initialState))]) 350 | // explored is where we've been 351 | var explored = Dictionary() 352 | explored[initialState] = 0 353 | // keep going while there is more to explore 354 | while let currentNode = frontier.pop() { 355 | let currentState = currentNode.state 356 | // if we found the goal, we're done 357 | if goalTestFn(currentState) { return currentNode } 358 | // check where we can go next and haven't explored 359 | for child in successorFn(currentState) { 360 | let newcost = currentNode.cost + 1 //1 assumes a grid, there should be a cost function for more sophisticated applications 361 | if (explored[child] == nil) || (explored[child]! > newcost) { 362 | explored[child] = newcost 363 | frontier.push(Node(state: child, parent: currentNode, cost: newcost, heuristic: heuristicFn(child))) 364 | } 365 | } 366 | } 367 | return nil // never found the goal 368 | } 369 | 370 | var maze3 = generateMaze(rows: 10, columns: 10, sparseness: 0.2) 371 | if let solution = astar(initialState: start, goalTestFn: goalTest, successorFn: successorsForMaze(maze3), heuristicFn: manhattanDistance) { 372 | let path = nodeToPath(solution) 373 | markMaze(&maze3, path: path, start: start, goal: goal) 374 | printMaze(maze3) 375 | } 376 | 377 | /// Missionaries and Cannibals 378 | 379 | let maxNum = 3 // max number of missionaries or cannibals 380 | 381 | struct MCState: Hashable, CustomStringConvertible { 382 | let missionaries: Int 383 | let cannibals: Int 384 | let boat: Bool 385 | var hashValue: Int { return missionaries * 10 + cannibals + (boat ? 1000 : 2000) } 386 | var description: String { 387 | let wm = missionaries // west bank missionaries 388 | let wc = cannibals // west bank cannibals 389 | let em = maxNum - wm // east bank missionaries 390 | let ec = maxNum - wc // east bank cannibals 391 | var description = "On the west bank there are \(wm) missionaries and \(wc) cannibals.\n" 392 | description += "On the east bank there are \(em) missionaries and \(ec) cannibals.\n" 393 | description += "The boat is on the \(boat ? "west" : "east") bank.\n" 394 | return description 395 | } 396 | } 397 | 398 | func ==(lhs: MCState, rhs: MCState) -> Bool { 399 | return lhs.hashValue == rhs.hashValue 400 | } 401 | 402 | func goalTestMC(state: MCState) -> Bool { 403 | return state == MCState(missionaries: 0, cannibals: 0, boat: false) 404 | } 405 | 406 | func isLegalMC(state: MCState) -> Bool { 407 | let wm = state.missionaries // west bank missionaries 408 | let wc = state.cannibals // west bank cannibals 409 | let em = maxNum - wm // east bank missionaries 410 | let ec = maxNum - wc // east bank cannibals 411 | // check there's not more cannibals than missionaries 412 | if wm < wc && wm > 0 { return false } 413 | if em < ec && em > 0 { return false } 414 | return true 415 | } 416 | 417 | func successorsMC(state: MCState) -> [MCState] { 418 | let wm = state.missionaries // west bank missionaries 419 | let wc = state.cannibals // west bank cannibals 420 | let em = maxNum - wm // east bank missionaries 421 | let ec = maxNum - wc // east bank cannibals 422 | var sucs: [MCState] = [MCState]() // next states 423 | 424 | if state.boat { // boat on west bank 425 | if wm > 1 { 426 | sucs.append(MCState(missionaries: wm - 2, cannibals: wc, boat: !state.boat)) 427 | } 428 | if wm > 0 { 429 | sucs.append(MCState(missionaries: wm - 1, cannibals: wc, boat: !state.boat)) 430 | } 431 | if wc > 1 { 432 | sucs.append(MCState(missionaries: wm, cannibals: wc - 2, boat: !state.boat)) 433 | } 434 | if wc > 0 { 435 | sucs.append(MCState(missionaries: wm, cannibals: wc - 1, boat: !state.boat)) 436 | } 437 | if (wc > 0) && (wm > 0){ 438 | sucs.append(MCState(missionaries: wm - 1, cannibals: wc - 1, boat: !state.boat)) 439 | } 440 | } else { // boat on east bank 441 | if em > 1 { 442 | sucs.append(MCState(missionaries: wm + 2, cannibals: wc, boat: !state.boat)) 443 | } 444 | if em > 0 { 445 | sucs.append(MCState(missionaries: wm + 1, cannibals: wc, boat: !state.boat)) 446 | } 447 | if ec > 1 { 448 | sucs.append(MCState(missionaries: wm, cannibals: wc + 2, boat: !state.boat)) 449 | } 450 | if ec > 0 { 451 | sucs.append(MCState(missionaries: wm, cannibals: wc + 1, boat: !state.boat)) 452 | } 453 | if (ec > 0) && (em > 0){ 454 | sucs.append(MCState(missionaries: wm + 1, cannibals: wc + 1, boat: !state.boat)) 455 | } 456 | } 457 | 458 | return sucs.filter{ isLegalMC(state: $0) } 459 | } 460 | 461 | func printMCSolution(path: [MCState]) { 462 | var oldState = path.first! 463 | print(oldState) 464 | for currentState in path[1.. { 23 | /// The variables in the CSP to be constrained. 24 | let variables: [V] 25 | /// The domains - every variable should have an associated domain. 26 | let domains: [V: [D]] 27 | /// The constraints on the variables. 28 | var constraints = Dictionary]>() 29 | 30 | /// You should create the variables and domains before initializing the CSP. 31 | public init (variables: [V], domains:[V: [D]]) { 32 | self.variables = variables 33 | self.domains = domains 34 | for variable in variables { 35 | constraints[variable] = [Constraint]() 36 | if domains[variable] == nil { 37 | print("Error: Missing domain for variable \(variable).") 38 | } 39 | } 40 | } 41 | 42 | /// Add a constraint to the CSP. It will automatically be applied to all the variables it includes. It should only include variables actually in the CSP. 43 | /// 44 | /// - parameter constraint: The constraint to add. 45 | public mutating func addConstraint(_ constraint: Constraint) { 46 | for variable in constraint.vars { 47 | if !variables.contains(variable) { 48 | print("Error: Could not find variable \(variable) from constraint \(constraint) in CSP.") 49 | } 50 | constraints[variable]?.append(constraint) 51 | } 52 | } 53 | } 54 | 55 | /// The base class of all constraints. 56 | open class Constraint { 57 | /// All subclasses should override this method. It defines whether a constraint has successfully been satisfied 58 | /// - parameter assignment: Potential domain selections for variables that are part of the constraint. 59 | /// - returns: Whether the constraint is satisfied. 60 | func isSatisfied(assignment: Dictionary) -> Bool { 61 | return true 62 | } 63 | /// The variables that make up the constraint. 64 | var vars: [V] { return [] } 65 | } 66 | 67 | /// the meat of the backtrack algorithm - a recursive depth first search 68 | /// 69 | /// - parameter csp: The CSP to operate on. 70 | /// - parameter assignment: Optionally, an already partially completed assignment. 71 | /// - returns: the assignment (solution), or nil if none can be found 72 | public func backtrackingSearch(csp: CSP, assignment: Dictionary = Dictionary()) -> Dictionary? 73 | { 74 | // assignment is complete if it has as many assignments as there are variables 75 | if assignment.count == csp.variables.count { return assignment } // base case 76 | 77 | // what are the unassigned variables? 78 | let unassigned = csp.variables.lazy.filter({ assignment[$0] == nil }) 79 | 80 | // get the domain of the first unassigned variable 81 | if let variable: V = unassigned.first, let domain = csp.domains[variable] { 82 | // try each value in the domain 83 | for value in domain { 84 | var localAssignment = assignment 85 | localAssignment[variable] = value 86 | // if the value is consistent with the current assignment we continue 87 | if isConsistent(variable: variable, value: value, assignment: localAssignment, csp: csp) { 88 | // if as we go down the tree we get a complete assignment, return it 89 | if let result = backtrackingSearch(csp: csp, assignment: localAssignment) { 90 | return result 91 | } 92 | } 93 | } 94 | } 95 | return nil // no solution 96 | } 97 | 98 | /// check if the value assignment is consistent by checking all constraints of the variable 99 | func isConsistent(variable: V, value: D, assignment: Dictionary, csp: CSP) -> Bool { 100 | for constraint in csp.constraints[variable] ?? [] { 101 | if !constraint.isSatisfied(assignment: assignment) { 102 | return false 103 | } 104 | } 105 | return true 106 | } 107 | 108 | /// ###Australian Map Coloring Problem 109 | final class MapColoringConstraint: Constraint { 110 | let place1: String 111 | let place2: String 112 | final override var vars: [String] { return [place1, place2] } 113 | 114 | init(place1: String, place2: String) { 115 | self.place1 = place1 116 | self.place2 = place2 117 | } 118 | 119 | override func isSatisfied(assignment: Dictionary) -> Bool { 120 | // if either variable is not in the assignment then it must be consistent 121 | // since they still have their domain 122 | if assignment[place1] == nil || assignment[place2] == nil { 123 | return true 124 | } 125 | // check that the color of var1 does not equal var2 126 | return assignment[place1] != assignment[place2] 127 | } 128 | } 129 | 130 | let variables: [String] = ["Western Australia", "Northern Territory", "South Australia", "Queensland", "New South Wales", "Victoria", "Tasmania"] 131 | var domains = Dictionary() 132 | for variable in variables { 133 | domains[variable] = ["r", "g", "b"] 134 | } 135 | 136 | var csp = CSP(variables: variables, domains: domains) 137 | csp.addConstraint(MapColoringConstraint(place1: "Western Australia", place2: "Northern Territory")) 138 | csp.addConstraint(MapColoringConstraint(place1: "Western Australia", place2: "South Australia")) 139 | csp.addConstraint(MapColoringConstraint(place1: "South Australia", place2: "Northern Territory")) 140 | csp.addConstraint(MapColoringConstraint(place1: "Queensland", place2: "Northern Territory")) 141 | csp.addConstraint(MapColoringConstraint(place1: "Queensland", 142 | place2: "South Australia")) 143 | csp.addConstraint(MapColoringConstraint(place1: "Queensland", place2: "New South Wales")) 144 | csp.addConstraint(MapColoringConstraint(place1: "New South Wales", place2: "South Australia")) 145 | csp.addConstraint(MapColoringConstraint(place1: "Victoria", place2: "South Australia")) 146 | csp.addConstraint(MapColoringConstraint(place1: "Victoria",place2: "New South Wales")) 147 | 148 | if let solution = backtrackingSearch(csp: csp) { 149 | print(solution) 150 | } else { print("Couldn't find solution!") } 151 | 152 | /// ###Eight Queens Problem 153 | 154 | final class QueensConstraint: Constraint { 155 | let columns: [Int] 156 | final override var vars: [Int] { return columns } 157 | 158 | init(columns: [Int]) { 159 | self.columns = columns 160 | } 161 | 162 | override func isSatisfied(assignment: Dictionary) -> Bool { 163 | for (q1c, q1r) in assignment { // q1c = queen 1 column, q1r = queen 1 row 164 | if (q1c >= vars.count) { 165 | break 166 | } 167 | for q2c in (q1c + 1)...vars.count { // queen 2 column 168 | if let q2r = assignment[q2c] { // queen 2 row 169 | if q1r == q2r { return false } // rows same? 170 | if abs(q1r - q2r) == abs(q1c - q2c) { return false } // same diagonal? 171 | } 172 | } 173 | } 174 | 175 | return true 176 | } 177 | } 178 | 179 | let cols: [Int] = [Int](1...8) 180 | var rows = Dictionary() 181 | for variable in cols { 182 | rows[variable] = [Int](1...8) 183 | } 184 | 185 | var qcsp = CSP(variables: cols, domains: rows) 186 | qcsp.addConstraint(QueensConstraint(columns: cols)) 187 | if let solution = backtrackingSearch(csp: qcsp) { 188 | print(solution) 189 | } else { print("Couldn't find solution!") } 190 | 191 | /// ###Word Search Problem 192 | 193 | // notice not too dissimilar from our Maze code from chapter 2 194 | typealias Grid = [[Character]] 195 | 196 | // A point on the grid 197 | struct GridLocation: Hashable { 198 | let row: Int 199 | let col: Int 200 | var hashValue: Int { return row.hashValue ^ col.hashValue } 201 | } 202 | func == (lhs: GridLocation, rhs: GridLocation) -> Bool { 203 | return lhs.row == rhs.row && lhs.col == rhs.col 204 | } 205 | 206 | // All the letters in our word search 207 | let ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 208 | 209 | // randomly inserted with characters 210 | func generateGrid(rows: Int, columns: Int) -> Grid { 211 | // initialize grid full of empty spaces 212 | var grid: Grid = Grid(repeating: [Character](repeating: " ", count: columns), count: rows) 213 | // replace spaces with random letters 214 | for row in 0.. [[GridLocation]] { 234 | var domain: [[GridLocation]] = [[GridLocation]]() 235 | let height = grid.count 236 | let width = grid[0].count 237 | let wordLength = word.count 238 | for row in 0..= 0) { 255 | domain.append(rows.map({GridLocation(row: $0, col: col - ($0 - row))})) 256 | } 257 | } 258 | } 259 | } 260 | return domain 261 | } 262 | 263 | 264 | final class WordSearchConstraint: Constraint { 265 | let words: [String] 266 | final override var vars: [String] { return words } 267 | 268 | init(words: [String]) { 269 | self.words = words 270 | } 271 | 272 | override func isSatisfied(assignment: Dictionary) -> Bool { 273 | if Set(assignment.values.flatMap({$0})).count < assignment.values.flatMap({$0}).count { 274 | return false 275 | } 276 | 277 | return true 278 | } 279 | } 280 | 281 | // May be commented out because it takes a long time to execute! 282 | // Uncomment all of the following 283 | // lines to see the word search in action. 284 | 285 | let words: [String] = ["MATTHEW", "JOE", "MARY", "SARAH", "SALLY"] 286 | var locations = Dictionary() 287 | for word in words { 288 | locations[word] = generateDomain(word: word, grid: grid) 289 | } 290 | 291 | var wordsearch = CSP(variables: words, domains: locations) 292 | wordsearch.addConstraint(WordSearchConstraint(words: words)) 293 | if let solution = backtrackingSearch(csp: wordsearch) { 294 | for (word, gridLocations) in solution { 295 | let gridLocs = arc4random_uniform(2) > 0 ? gridLocations : gridLocations.reversed() // randomly reverse word half the time 296 | for (index, letter) in word.enumerated() { 297 | let (row, col) = (gridLocs[index].row, gridLocations[index].col) 298 | grid[row][col] = letter 299 | } 300 | } 301 | printGrid(grid) 302 | } else { print("Couldn't find solution!") } 303 | 304 | /// ###SEND+MORE=MONEY 305 | 306 | final class SendMoreMoneyConstraint: Constraint { 307 | let letters: [Character] 308 | final override var vars: [Character] { return letters } 309 | init(variables: [Character]) { 310 | letters = variables 311 | } 312 | 313 | override func isSatisfied(assignment: Dictionary) -> Bool { 314 | // if there are duplicate values then it's not correct 315 | let d = Set(assignment.values) 316 | if d.count < assignment.count { 317 | return false 318 | } 319 | 320 | // if all variables have been assigned, check if it adds up correctly 321 | if assignment.count == letters.count { 322 | if let s = assignment["S"], let e = assignment["E"], let n = assignment["N"], let d = assignment["D"], let m = assignment["M"], let o = assignment["O"], let r = assignment["R"], let y = assignment["Y"] { 323 | let send: Int = s * 1000 + e * 100 + n * 10 + d 324 | let more: Int = m * 1000 + o * 100 + r * 10 + e 325 | let money: Int = m * 10000 + o * 1000 + n * 100 + e * 10 + y 326 | if (send + more) == money { 327 | return true // answer found 328 | } 329 | } 330 | return false // this full assignment doesn't work 331 | } 332 | 333 | // until we have all of the variables assigned, the assignment is valid 334 | return true 335 | } 336 | } 337 | 338 | let letters: [Character] = ["S", "E", "N", "D", "M", "O", "R", "Y"] 339 | var possibleDigits = Dictionary() 340 | for letter in letters { 341 | possibleDigits[letter] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 342 | } 343 | possibleDigits["S"] = [9] 344 | possibleDigits["M"] = [1] 345 | possibleDigits["O"] = [0] 346 | 347 | var smmcsp = CSP(variables: letters, domains: possibleDigits) 348 | let smmcon = SendMoreMoneyConstraint(variables: letters) 349 | smmcsp.addConstraint(smmcon) 350 | 351 | if let solution = backtrackingSearch(csp: smmcsp) { 352 | print(solution) 353 | } else { print("Couldn't find solution!") } 354 | 355 | //: [Next](@next) 356 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Pages/Chapter 4.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | // Classic Computer Science Problems in Swift Chapter 4 Source 4 | 5 | // Copyright 2017 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | public protocol Edge: CustomStringConvertible { 20 | var u: Int { get set } // index of the "from" vertex 21 | var v: Int { get set } // index of the "to" vertex 22 | var reversed: Edge { get } 23 | } 24 | 25 | protocol Graph: class, CustomStringConvertible { 26 | associatedtype VertexType: Equatable 27 | associatedtype EdgeType: Edge 28 | var vertices: [VertexType] { get set } 29 | var edges: [[EdgeType]] { get set } 30 | } 31 | 32 | extension Graph { 33 | /// How many vertices are in the graph? 34 | public var vertexCount: Int { return vertices.count } 35 | 36 | /// How many edges are in the graph? 37 | public var edgeCount: Int { return edges.joined().count } 38 | 39 | /// Get a vertex by its index. 40 | /// 41 | /// - parameter index: The index of the vertex. 42 | /// - returns: The vertex at i. 43 | public func vertexAtIndex(_ index: Int) -> VertexType { 44 | return vertices[index] 45 | } 46 | 47 | /// Find the first occurence of a vertex if it exists. 48 | /// 49 | /// - parameter vertex: The vertex you are looking for. 50 | /// - returns: The index of the vertex. Return nil if it can't find it. 51 | public func indexOfVertex(_ vertex: VertexType) -> Int? { 52 | if let i = vertices.index(of: vertex) { 53 | return i 54 | } 55 | return nil 56 | } 57 | 58 | /// Find all of the neighbors of a vertex at a given index. 59 | /// 60 | /// - parameter index: The index for the vertex to find the neighbors of. 61 | /// - returns: An array of the neighbor vertices. 62 | public func neighborsForIndex(_ index: Int) -> [VertexType] { 63 | return edges[index].map({self.vertices[$0.v]}) 64 | } 65 | 66 | /// Find all of the neighbors of a given Vertex. 67 | /// 68 | /// - parameter vertex: The vertex to find the neighbors of. 69 | /// - returns: An optional array of the neighbor vertices. 70 | public func neighborsForVertex(_ vertex: VertexType) -> [VertexType]? { 71 | if let i = indexOfVertex(vertex) { 72 | return neighborsForIndex(i) 73 | } 74 | return nil 75 | } 76 | 77 | /// Find all of the edges of a vertex at a given index. 78 | /// 79 | /// - parameter index: The index for the vertex to find the children of. 80 | public func edgesForIndex(_ index: Int) -> [EdgeType] { 81 | return edges[index] 82 | } 83 | 84 | /// Find all of the edges of a given vertex. 85 | /// 86 | /// - parameter vertex: The vertex to find the edges of. 87 | public func edgesForVertex(_ vertex: VertexType) -> [EdgeType]? { 88 | if let i = indexOfVertex(vertex) { 89 | return edgesForIndex(i) 90 | } 91 | return nil 92 | } 93 | 94 | /// Add a vertex to the graph. 95 | /// 96 | /// - parameter v: The vertex to be added. 97 | /// - returns: The index where the vertex was added. 98 | public func addVertex(_ v: VertexType) -> Int { 99 | vertices.append(v) 100 | edges.append([EdgeType]()) 101 | return vertices.count - 1 102 | } 103 | 104 | /// Add an edge to the graph. 105 | /// 106 | /// - parameter e: The edge to add. 107 | public func addEdge(_ e: EdgeType) { 108 | edges[e.u].append(e) 109 | edges[e.v].append(e.reversed as! EdgeType) 110 | } 111 | } 112 | 113 | /// A basic unweighted edge. 114 | open class UnweightedEdge: Edge { 115 | public var u: Int // "from" vertex 116 | public var v: Int // "to" vertex 117 | public var reversed: Edge { 118 | return UnweightedEdge(u: v, v: u) 119 | } 120 | 121 | public init(u: Int, v: Int) { 122 | self.u = u 123 | self.v = v 124 | } 125 | 126 | //MARK: CustomStringConvertable 127 | public var description: String { 128 | return "\(u) <-> \(v)" 129 | } 130 | } 131 | 132 | /// A graph with only unweighted edges. 133 | open class UnweightedGraph: Graph { 134 | var vertices: [V] = [V]() 135 | var edges: [[UnweightedEdge]] = [[UnweightedEdge]]() //adjacency lists 136 | 137 | public init() { 138 | } 139 | 140 | public init(vertices: [V]) { 141 | for vertex in vertices { 142 | _ = self.addVertex(vertex) 143 | } 144 | } 145 | 146 | /// This is a convenience method that adds an unweighted edge. 147 | /// 148 | /// - parameter from: The starting vertex's index. 149 | /// - parameter to: The ending vertex's index. 150 | public func addEdge(from: Int, to: Int) { 151 | addEdge(UnweightedEdge(u: from, v: to)) 152 | } 153 | 154 | /// This is a convenience method that adds an unweighted, undirected edge between the first occurence of two vertices. 155 | /// 156 | /// - parameter from: The starting vertex. 157 | /// - parameter to: The ending vertex. 158 | public func addEdge(from: V, to: V) { 159 | if let u = indexOfVertex(from) { 160 | if let v = indexOfVertex(to) { 161 | addEdge(UnweightedEdge(u: u, v: v)) 162 | } 163 | } 164 | } 165 | 166 | /// MARK: Implement CustomStringConvertible 167 | public var description: String { 168 | var d: String = "" 169 | for i in 0.. = UnweightedGraph(vertices: ["Seattle", "San Francisco", "Los Angeles", "Riverside", "Phoenix", "Chicago", "Boston", "New York", "Atlanta", "Miami", "Dallas", "Houston", "Detroit", "Philadelphia", "Washington"]) 180 | 181 | cityGraph.addEdge(from: "Seattle", to: "Chicago") 182 | cityGraph.addEdge(from: "Seattle", to: "San Francisco") 183 | cityGraph.addEdge(from: "San Francisco", to: "Riverside") 184 | cityGraph.addEdge(from: "San Francisco", to: "Los Angeles") 185 | cityGraph.addEdge(from: "Los Angeles", to: "Riverside") 186 | cityGraph.addEdge(from: "Los Angeles", to: "Phoenix") 187 | cityGraph.addEdge(from: "Riverside", to: "Phoenix") 188 | cityGraph.addEdge(from: "Riverside", to: "Chicago") 189 | cityGraph.addEdge(from: "Phoenix", to: "Dallas") 190 | cityGraph.addEdge(from: "Phoenix", to: "Houston") 191 | cityGraph.addEdge(from: "Dallas", to: "Chicago") 192 | cityGraph.addEdge(from: "Dallas", to: "Atlanta") 193 | cityGraph.addEdge(from: "Dallas", to: "Houston") 194 | cityGraph.addEdge(from: "Houston", to: "Atlanta") 195 | cityGraph.addEdge(from: "Houston", to: "Miami") 196 | cityGraph.addEdge(from: "Atlanta", to: "Chicago") 197 | cityGraph.addEdge(from: "Atlanta", to: "Washington") 198 | cityGraph.addEdge(from: "Atlanta", to: "Miami") 199 | cityGraph.addEdge(from: "Miami", to: "Washington") 200 | cityGraph.addEdge(from: "Chicago", to: "Detroit") 201 | cityGraph.addEdge(from: "Detroit", to: "Boston") 202 | cityGraph.addEdge(from: "Detroit", to: "Washington") 203 | cityGraph.addEdge(from: "Detroit", to: "New York") 204 | cityGraph.addEdge(from: "Boston", to: "New York") 205 | cityGraph.addEdge(from: "New York", to: "Philadelphia") 206 | cityGraph.addEdge(from: "Philadelphia", to: "Washington") 207 | 208 | print(cityGraph) 209 | 210 | public typealias Path = [Edge] 211 | 212 | extension Graph { 213 | /// Prints a path in a readable format 214 | public func printPath(_ path: Path) { 215 | for edge in path { 216 | print("\(vertexAtIndex(edge.u)) > \(vertexAtIndex(edge.v))") 217 | } 218 | } 219 | } 220 | 221 | ///bfs 222 | public class Queue { 223 | private var container: [T] = [T]() 224 | public var isEmpty: Bool { return container.isEmpty } 225 | public func push(_ thing: T) { container.append(thing) } 226 | public func pop() -> T { return container.removeFirst() } 227 | } 228 | 229 | /// Takes a dictionary of edges to reach each node and returns an array of edges 230 | /// that goes from `from` to `to` 231 | public func pathDictToPath(from: Int, to: Int, pathDict:[Int: Edge]) -> Path { 232 | if pathDict.count == 0 { 233 | return [] 234 | } 235 | var edgePath: Path = Path() 236 | var e: Edge = pathDict[to]! 237 | edgePath.append(e) 238 | while (e.u != from) { 239 | e = pathDict[e.u]! 240 | edgePath.append(e) 241 | } 242 | return Array(edgePath.reversed()) 243 | } 244 | 245 | extension Graph { 246 | //returns a path to the goal vertex 247 | func bfs(initialVertex: VertexType, goalTestFn: (VertexType) -> Bool) -> Path? { 248 | guard let startIndex = indexOfVertex(initialVertex) else { return nil } 249 | // frontier is where we've yet to go 250 | let frontier: Queue = Queue() 251 | frontier.push(startIndex) 252 | // explored is where we've been 253 | var explored: Set = Set() 254 | explored.insert(startIndex) 255 | // how did we get to each vertex 256 | var pathDict: [Int: EdgeType] = [Int: EdgeType]() 257 | // keep going while there is more to explore 258 | while !frontier.isEmpty { 259 | let currentIndex = frontier.pop() 260 | let currentVertex = vertexAtIndex(currentIndex) 261 | // if we found the goal, we're done 262 | if goalTestFn(currentVertex) { 263 | return pathDictToPath(from: startIndex, to: currentIndex, pathDict: pathDict) 264 | } 265 | // check where we can go next and haven't explored 266 | for edge in edgesForIndex(currentIndex) where !explored.contains(edge.v) { 267 | explored.insert(edge.v) 268 | frontier.push(edge.v) 269 | pathDict[edge.v] = edge 270 | } 271 | } 272 | return nil // never found the goal 273 | } 274 | } 275 | 276 | 277 | 278 | if let bostonToMiami = cityGraph.bfs(initialVertex: "Boston", goalTestFn: { $0 == "Miami" }) { 279 | cityGraph.printPath(bostonToMiami) 280 | } 281 | 282 | /// This protocol is needed for Dijkstra's algorithm - we need weights in weighted graphs 283 | /// to be able to be added together 284 | public protocol Summable { 285 | static func +(lhs: Self, rhs: Self) -> Self 286 | } 287 | 288 | extension Int: Summable {} 289 | extension Double: Summable {} 290 | extension Float: Summable {} 291 | 292 | /// A weighted edge, who's weight subscribes to Comparable. 293 | open class WeightedEdge: Edge, Comparable { 294 | public var u: Int 295 | public var v: Int 296 | public let weight: W 297 | 298 | public var reversed: Edge { 299 | return WeightedEdge(u: v, v: u, weight: weight) 300 | } 301 | 302 | public init(u: Int, v: Int, weight: W) { 303 | self.weight = weight 304 | self.u = u 305 | self.v = v 306 | } 307 | 308 | //Implement CustomStringConvertible protocol 309 | public var description: String { 310 | return "\(u) <\(weight)> \(v)" 311 | } 312 | 313 | //MARK: Operator Overloads for Comparable 314 | static public func == (lhs: WeightedEdge, rhs: WeightedEdge) -> Bool { 315 | return lhs.u == rhs.u && lhs.v == rhs.v && lhs.weight == rhs.weight 316 | } 317 | 318 | static public func < (lhs: WeightedEdge, rhs: WeightedEdge) -> Bool { 319 | return lhs.weight < rhs.weight 320 | } 321 | } 322 | 323 | /// A subclass of Graph that has convenience methods for adding and removing WeightedEdges. All added Edges should have the same generic Comparable type W as the WeightedGraph itself. 324 | open class WeightedGraph: Graph { 325 | var vertices: [V] = [V]() 326 | var edges: [[WeightedEdge]] = [[WeightedEdge]]() //adjacency lists 327 | 328 | public init() { 329 | } 330 | 331 | public init(vertices: [V]) { 332 | for vertex in vertices { 333 | _ = self.addVertex(vertex) 334 | } 335 | } 336 | 337 | /// Find all of the neighbors of a vertex at a given index. 338 | /// 339 | /// - parameter index: The index for the vertex to find the neighbors of. 340 | /// - returns: An array of tuples including the vertices as the first element and the weights as the second element. 341 | public func neighborsForIndexWithWeights(_ index: Int) -> [(V, W)] { 342 | var distanceTuples: [(V, W)] = [(V, W)]() 343 | for edge in edges[index] { 344 | distanceTuples += [(vertices[edge.v], edge.weight)] 345 | } 346 | return distanceTuples 347 | } 348 | 349 | /// This is a convenience method that adds a weighted edge. 350 | /// 351 | /// - parameter from: The starting vertex's index. 352 | /// - parameter to: The ending vertex's index. 353 | /// - parameter weight: the Weight of the edge to add. 354 | public func addEdge(from: Int, to: Int, weight:W) { 355 | addEdge(WeightedEdge(u: from, v: to, weight: weight)) 356 | } 357 | 358 | /// This is a convenience method that adds a weighted edge between the first occurence of two vertices. It takes O(n) time. 359 | /// 360 | /// - parameter from: The starting vertex. 361 | /// - parameter to: The ending vertex. 362 | /// - parameter weight: the Weight of the edge to add. 363 | public func addEdge(from: V, to: V, weight: W) { 364 | if let u = indexOfVertex(from) { 365 | if let v = indexOfVertex(to) { 366 | addEdge(WeightedEdge(u: u, v: v, weight:weight)) 367 | } 368 | } 369 | } 370 | 371 | //Implement Printable protocol 372 | public var description: String { 373 | var d: String = "" 374 | for i in 0.. = WeightedGraph(vertices: ["Seattle", "San Francisco", "Los Angeles", "Riverside", "Phoenix", "Chicago", "Boston", "New York", "Atlanta", "Miami", "Dallas", "Houston", "Detroit", "Philadelphia", "Washington"]) 382 | 383 | cityGraph2.addEdge(from: "Seattle", to: "Chicago", weight: 1737) 384 | cityGraph2.addEdge(from: "Seattle", to: "San Francisco", weight: 678) 385 | cityGraph2.addEdge(from: "San Francisco", to: "Riverside", weight: 386) 386 | cityGraph2.addEdge(from: "San Francisco", to: "Los Angeles", weight: 348) 387 | cityGraph2.addEdge(from: "Los Angeles", to: "Riverside", weight: 50) 388 | cityGraph2.addEdge(from: "Los Angeles", to: "Phoenix", weight: 357) 389 | cityGraph2.addEdge(from: "Riverside", to: "Phoenix", weight: 307) 390 | cityGraph2.addEdge(from: "Riverside", to: "Chicago", weight: 1704) 391 | cityGraph2.addEdge(from: "Phoenix", to: "Dallas", weight: 887) 392 | cityGraph2.addEdge(from: "Phoenix", to: "Houston", weight: 1015) 393 | cityGraph2.addEdge(from: "Dallas", to: "Chicago", weight: 805) 394 | cityGraph2.addEdge(from: "Dallas", to: "Atlanta", weight: 721) 395 | cityGraph2.addEdge(from: "Dallas", to: "Houston", weight: 225) 396 | cityGraph2.addEdge(from: "Houston", to: "Atlanta", weight: 702) 397 | cityGraph2.addEdge(from: "Houston", to: "Miami", weight: 968) 398 | cityGraph2.addEdge(from: "Atlanta", to: "Chicago", weight: 588) 399 | cityGraph2.addEdge(from: "Atlanta", to: "Washington", weight: 543) 400 | cityGraph2.addEdge(from: "Atlanta", to: "Miami", weight: 604) 401 | cityGraph2.addEdge(from: "Miami", to: "Washington", weight: 923) 402 | cityGraph2.addEdge(from: "Chicago", to: "Detroit", weight: 238) 403 | cityGraph2.addEdge(from: "Detroit", to: "Boston", weight: 613) 404 | cityGraph2.addEdge(from: "Detroit", to: "Washington", weight: 396) 405 | cityGraph2.addEdge(from: "Detroit", to: "New York", weight: 482) 406 | cityGraph2.addEdge(from: "Boston", to: "New York", weight: 190) 407 | cityGraph2.addEdge(from: "New York", to: "Philadelphia", weight: 81) 408 | cityGraph2.addEdge(from: "Philadelphia", to: "Washington", weight: 123) 409 | 410 | print(cityGraph2) 411 | 412 | /// Find the total weight of an array of weighted edges 413 | /// - parameter edges The edge array to find the total weight of. 414 | public func totalWeight(_ edges: [WeightedEdge]) -> W? { 415 | guard let firstWeight = edges.first?.weight else { return nil } 416 | return edges.dropFirst().reduce(firstWeight) { (result, next) -> W in 417 | return result + next.weight 418 | } 419 | } 420 | 421 | /// Extensions to WeightedGraph for building a Minimum-Spanning Tree (MST) 422 | public extension WeightedGraph { 423 | typealias WeightedPath = [WeightedEdge] 424 | // Citation: Based on Algorithms 4th Edition by Sedgewick, Wayne pg 619 425 | 426 | /// Find the minimum spanning tree in a weighted graph. This is the set of edges 427 | /// that touches every vertex in the graph and is of minimal combined weight. This function 428 | /// uses Jarnik's Algorithm (aka Prim's Algorithm) and so assumes the graph has 429 | /// undirected edges. For a graph with directed edges, the result may be incorrect. Also, 430 | /// if the graph is not fully connected, the tree will only span the connected component from which 431 | /// the starting vertex belongs. 432 | /// 433 | /// - parameter start: The index of the vertex to start creating the MST from. 434 | /// - returns: An array of WeightedEdges containing the minimum spanning tree, or nil if the starting vertex is invalid. If there are is only one vertex connected to the starting vertex, an empty list is returned. 435 | public func mst(start: Int = 0) -> WeightedPath? { 436 | if start > (vertexCount - 1) || start < 0 { return nil } 437 | var result: [WeightedEdge] = [WeightedEdge]() // the final MST goes in here 438 | var pq: PriorityQueue> = PriorityQueue>(ascending: true) // minPQ 439 | var visited: [Bool] = Array(repeating: false, count: vertexCount) // already been to these 440 | 441 | func visit(_ index: Int) { 442 | visited[index] = true // mark as visited 443 | for edge in edgesForIndex(index) { // add all edges coming from here to pq 444 | if !visited[edge.v] { pq.push(edge) } 445 | } 446 | } 447 | 448 | visit(start) // the first vertex is where everything begins 449 | 450 | while let edge = pq.pop() { // keep going as long as there are edges to process 451 | if visited[edge.v] { continue } // if we've been here, ignore 452 | result.append(edge) // otherwise this is the current smallest so add it to the result set 453 | visit(edge.v) // visit where this connects 454 | } 455 | 456 | return result 457 | } 458 | 459 | /// Pretty-print an edge list returned from an MST 460 | /// - parameter edges The edge array representing the MST 461 | public func printWeightedPath(_ weightedPath: WeightedPath) { 462 | for edge in weightedPath { 463 | print("\(vertexAtIndex(edge.u)) \(edge.weight)> \(vertexAtIndex(edge.v))") 464 | } 465 | if let tw = totalWeight(weightedPath) { 466 | print("Total Weight: \(tw)") 467 | } 468 | } 469 | } 470 | 471 | if let mst = cityGraph2.mst() { 472 | cityGraph2.printWeightedPath(mst) 473 | } 474 | 475 | //MARK: `WeightedGraph` extension for doing dijkstra 476 | 477 | public extension WeightedGraph { 478 | 479 | //MARK: Dijkstra Utilites 480 | 481 | /// Represents a node in the priority queue used 482 | /// for selecting the next 483 | struct DijkstraNode: Comparable, Equatable { 484 | let vertex: Int 485 | let distance: W 486 | 487 | public static func < (lhs: DijkstraNode, rhs: DijkstraNode) -> Bool { 488 | return lhs.distance < rhs.distance 489 | } 490 | 491 | public static func == (lhs: DijkstraNode, rhs: DijkstraNode) -> Bool { 492 | return lhs.distance == rhs.distance 493 | } 494 | } 495 | 496 | /// Finds the shortest paths from some route vertex to every other vertex in the graph. 497 | /// 498 | /// - parameter graph: The WeightedGraph to look within. 499 | /// - parameter root: The index of the root node to build the shortest paths from. 500 | /// - parameter startDistance: The distance to get to the root node (typically 0). 501 | /// - returns: Returns a tuple of two things: the first, an array containing the distances, the second, a dictionary containing the edge to reach each vertex. Use the function pathDictToPath() to convert the dictionary into something useful for a specific point. 502 | public func dijkstra(root: Int, startDistance: W) -> ([W?], [Int: WeightedEdge]) { 503 | var distances: [W?] = [W?](repeating: nil, count: vertexCount) // how far each vertex is from start 504 | distances[root] = startDistance // the start vertex is startDistance away 505 | var pq: PriorityQueue = PriorityQueue(ascending: true) 506 | var pathDict: [Int: WeightedEdge] = [Int: WeightedEdge]() // how we got to each vertex 507 | pq.push(DijkstraNode(vertex: root, distance: startDistance)) 508 | 509 | while let u = pq.pop()?.vertex { // explore the next closest vertex 510 | guard let distU = distances[u] else { continue } // should already have seen it 511 | for we in edgesForIndex(u) { // look at every edge/vertex from the vertex in question 512 | let distV = distances[we.v] // the old distance to this vertex 513 | if distV == nil || distV! > we.weight + distU { // if we have no old distance or we found a shorter path 514 | distances[we.v] = we.weight + distU // update the distance to this vertex 515 | pathDict[we.v] = we // update the edge on the shortest path to this vertex 516 | pq.push(DijkstraNode(vertex: we.v, distance: we.weight + distU)) // explore it soon 517 | } 518 | } 519 | } 520 | 521 | return (distances, pathDict) 522 | } 523 | 524 | 525 | /// A convenience version of dijkstra() that allows the supply of the root 526 | /// vertex instead of the index of the root vertex. 527 | public func dijkstra(root: V, startDistance: W) -> ([W?], [Int: WeightedEdge]) { 528 | if let u = indexOfVertex(root) { 529 | return dijkstra(root: u, startDistance: startDistance) 530 | } 531 | return ([], [:]) 532 | } 533 | 534 | /// Helper function to get easier access to Dijkstra results. 535 | public func distanceArrayToVertexDict(distances: [W?]) -> [V : W?] { 536 | var distanceDict: [V: W?] = [V: W?]() 537 | for i in 0..]) 552 | 553 | //: [Next](@next) 554 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Pages/Chapter 5.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | // Classic Computer Science Problems in Swift Chapter 5 Source 4 | 5 | // Copyright 2017 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation // for arc4random_uniform() and drand48() 20 | 21 | srand48(time(nil)) // seed random number generator for drand48() 22 | 23 | // A derivative of the Fisher-Yates algorithm to shuffle an array 24 | extension Array { 25 | public func shuffled() -> Array { 26 | var shuffledArray = self // value semantics (Array is Struct) makes this a copy 27 | if count < 2 { return shuffledArray } // already shuffled 28 | for i in (1.. Self 42 | func crossover(other: Self) -> (child1: Self, child2: Self) // combine with other to form children 43 | func mutate() // make a small change somewhere 44 | func prettyPrint() 45 | } 46 | 47 | open class GeneticAlgorithm { 48 | enum SelectionType { 49 | case roulette 50 | case tournament(UInt) // the UInt is the number of participants in the tournament 51 | } 52 | 53 | private let threshold: Double // at what fitness level to stop running 54 | private let maxGenerations: UInt // number of generations to run 55 | private let mutationChance: Double // probability of mutation for each individual in each generation 56 | private let crossoverChance: Double // probability of any two children being crossed each generation 57 | private let selectionType: SelectionType // which selection method? 58 | 59 | private var population: [ChromosomeType] // all of the individuals in a generation 60 | private var fitnessCache: [Double] // the fitness of each individual in the current generation 61 | private var fitnessSum: Double = -Double.greatestFiniteMagnitude // summed generation fitness 62 | 63 | init(size: UInt, threshold: Double, maxGenerations: UInt = 100, mutationChance: Double = 0.01, crossoverChance: Double = 0.7, selectionType: SelectionType = SelectionType.tournament(4)) { 64 | self.threshold = threshold 65 | self.maxGenerations = maxGenerations 66 | self.mutationChance = mutationChance 67 | self.crossoverChance = crossoverChance 68 | self.selectionType = selectionType 69 | 70 | population = [ChromosomeType]() // initialize the population with random chromosomes 71 | for _ in 0.. ChromosomeType { 79 | var pick = drand48() // chance of picking a particular one 80 | for (index, chance) in wheel.enumerated() { 81 | pick -= chance 82 | if pick <= 0 { // we had one that took us over, leads to a pick 83 | return population[index] 84 | } 85 | } 86 | return population[0] 87 | } 88 | 89 | // find k random individuals in the population and pick the best one 90 | private func pickTournament(numParticipants: UInt) -> ChromosomeType { 91 | var best: ChromosomeType = ChromosomeType.randomInstance() 92 | var bestFitness: Double = best.fitness 93 | for _ in 0.. bestFitness { 96 | bestFitness = fitnessCache[test] 97 | best = population[test] 98 | } 99 | } 100 | return best 101 | } 102 | 103 | private func reproduceAndReplace() { 104 | var newPopulation: [ChromosomeType] = [ChromosomeType]() // replacement population 105 | var chanceEach: [Double] = [Double]() // used for pickRoulette, chance of each individual being picked 106 | if case .roulette = selectionType { 107 | chanceEach = fitnessCache.map({return $0/fitnessSum}) 108 | } 109 | while newPopulation.count < population.count { 110 | var parents: (parent1: ChromosomeType, parent2: ChromosomeType) 111 | switch selectionType { // how to pick parents 112 | case let .tournament(k): 113 | parents = (parent1: pickTournament(numParticipants: k), parent2: pickTournament(numParticipants: k)) 114 | default: // don't have a case for roulette because no other option 115 | parents = (parent1: pickRoulette(wheel: chanceEach), parent2: pickRoulette(wheel: chanceEach)) 116 | } 117 | if drand48() < crossoverChance { // if crossover, produce children 118 | let children = parents.parent1.crossover(other: parents.parent2) 119 | newPopulation.append(children.child1) 120 | newPopulation.append(children.child2) 121 | } else { // no crossover, just use parents 122 | newPopulation.append(parents.parent1) 123 | newPopulation.append(parents.parent2) 124 | } 125 | } 126 | if newPopulation.count > population.count { // in case we had an odd population 127 | newPopulation.removeLast() 128 | } 129 | population = newPopulation 130 | } 131 | 132 | private func mutate() { 133 | for individual in population { // every individual could possibly be mutated each generation 134 | if drand48() < mutationChance { 135 | individual.mutate() 136 | } 137 | } 138 | } 139 | 140 | public func run() -> ChromosomeType { 141 | var best: ChromosomeType = ChromosomeType.randomInstance() // best in any run so far 142 | var bestFitness: Double = best.fitness 143 | for generation in 1...maxGenerations { // try maxGenerations of the genetic algorithm 144 | print("generation \(generation) best \(best.fitness) avg \(fitnessSum / Double(fitnessCache.count))") 145 | for (index, individual) in population.enumerated() { 146 | fitnessCache[index] = individual.fitness 147 | if fitnessCache[index] >= threshold { // early end; found something great 148 | return individual 149 | } 150 | if fitnessCache[index] > bestFitness { // best so far in any iteration 151 | bestFitness = fitnessCache[index] 152 | best = ChromosomeType(from: individual) 153 | } 154 | } 155 | fitnessSum = fitnessCache.reduce(0, +) 156 | reproduceAndReplace() 157 | mutate() 158 | } 159 | return best 160 | } 161 | } 162 | 163 | final class SimpleEquation: Chromosome { 164 | var x: Int = Int(arc4random_uniform(100)) 165 | var y: Int = Int(arc4random_uniform(100)) 166 | 167 | var fitness: Double { // 6x - x^2 + 4y - y^2 168 | return Double(6 * x - x * x + 4 * y - y * y) 169 | } 170 | 171 | init(from: SimpleEquation) { // like making a copy 172 | x = from.x 173 | y = from.y 174 | } 175 | 176 | init() {} 177 | 178 | static func randomInstance() -> SimpleEquation { 179 | return SimpleEquation() 180 | } 181 | 182 | func crossover(other: SimpleEquation) -> (child1: SimpleEquation, child2: SimpleEquation) { 183 | let child1 = SimpleEquation(from: self) 184 | let child2 = SimpleEquation(from: other) 185 | child1.y = other.y 186 | child2.y = self.y 187 | return (child1: child1, child2: child2) 188 | } 189 | 190 | func mutate() { 191 | if drand48() > 0.5 { // mutate x 192 | if drand48() > 0.5 { 193 | x += 1 194 | } else { 195 | x -= 1 196 | } 197 | } else { // otherwise mutate y 198 | if drand48() > 0.5 { 199 | y += 1 200 | } else { 201 | y -= 1 202 | } 203 | } 204 | } 205 | 206 | func prettyPrint() { 207 | print("x:\(x) y:\(y) fitness:\(fitness)") 208 | } 209 | } 210 | 211 | let se = GeneticAlgorithm(size: 10, threshold: 13.0, maxGenerations: 100, mutationChance: 0.1, crossoverChance: 0.7) 212 | let result1 = se.run() 213 | result1.fitness 214 | result1.prettyPrint() 215 | 216 | 217 | 218 | 219 | final class SendMoreMoney: Chromosome { 220 | var genes: [Character] 221 | static let letters: [Character] = ["S", "E", "N", "D", "M", "O", "R", "Y", " ", " "] 222 | 223 | var fitness: Double { 224 | if let s = genes.index(of: "S"), let e = genes.index(of: "E"), let n = genes.index(of: "N"), let d = genes.index(of: "D"), let m = genes.index(of: "M"), let o = genes.index(of: "O"), let r = genes.index(of: "R"), let y = genes.index(of: "Y") { 225 | let send: Int = s * 1000 + e * 100 + n * 10 + d 226 | let more: Int = m * 1000 + o * 100 + r * 10 + e 227 | let money: Int = m * 10000 + o * 1000 + n * 100 + e * 10 + y 228 | let difference = abs(money - (send + more)) 229 | return 1 / Double(difference + 1) 230 | } 231 | return 0 232 | } 233 | 234 | init(from: SendMoreMoney) { 235 | genes = from.genes 236 | } 237 | 238 | init(genes: [Character]) { 239 | self.genes = genes 240 | } 241 | 242 | static func randomInstance() -> SendMoreMoney { 243 | return SendMoreMoney(genes: letters.shuffled()) 244 | } 245 | 246 | func crossover(other: SendMoreMoney) -> (child1: SendMoreMoney, child2: SendMoreMoney) { 247 | let crossingPoint = Int(arc4random_uniform(UInt32(genes.count))) 248 | let childGenes1 = genes[0.. = GeneticAlgorithm(size: 100, threshold: 1.0, maxGenerations: 1000, mutationChance: 0.3, crossoverChance: 0.7, selectionType: .tournament(5)) 278 | let result2 = smm.run() 279 | result2.prettyPrint() 280 | 281 | //: [Next](@next) 282 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Pages/Chapter 6.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | // Classic Computer Science Problems in Swift Chapter 6 Source 4 | 5 | // Copyright 2017 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation // for pow(), srand48(), drand48() 20 | 21 | extension Array where Element == Double { 22 | var sum: Double { 23 | return self.reduce(0.0, +) 24 | } 25 | 26 | // Find the average (mean) 27 | var mean: Double { 28 | return sum / Double(self.count) 29 | } 30 | 31 | // Find the variance sum((Xi - mean)^2) / N 32 | var variance: Double { 33 | let mean = self.mean // cache so not recalculated for every element 34 | return self.map { pow(($0 - mean), 2) }.mean 35 | } 36 | 37 | // Find the standard deviation sqrt(variance) 38 | var std: Double { 39 | return sqrt(variance) 40 | } 41 | 42 | // Convert elements to respective z-scores (formula z-score = (x - mean) / std) 43 | var zscored: [Double] { 44 | let mean = self.mean 45 | let std = self.std 46 | return self.map{ std != 0 ? (($0 - mean) / std) : 0.0 } // avoid divide by zero 47 | } 48 | } 49 | 50 | let test: [Double] = [600, 470, 170, 430, 300] 51 | test.sum 52 | test.mean 53 | test.variance 54 | test.std 55 | test.zscored 56 | 57 | struct Random { 58 | private static var seeded = false 59 | 60 | // a random Double between *from* and *to*, assumes *from* < *to* 61 | static func double(from: Double, to: Double) -> Double { 62 | if !Random.seeded { 63 | srand48(time(nil)) 64 | Random.seeded = true 65 | } 66 | 67 | return (drand48() * (to - from)) + from 68 | } 69 | } 70 | 71 | public protocol DataPoint: CustomStringConvertible, Equatable { 72 | static var numDimensions: UInt { get } 73 | var dimensions: [Double] { get set } 74 | init(values: [Double]) 75 | } 76 | 77 | extension DataPoint { 78 | // euclidean distance 79 | func distance(to: PointType) -> Double { 80 | return sqrt(zip(dimensions, to.dimensions).map({ pow(($0.1 - $0.0), 2) }).sum) 81 | } 82 | } 83 | 84 | public struct Point3D: DataPoint { 85 | public static let numDimensions: UInt = 3 86 | public let x: Double 87 | public let y: Double 88 | public let z: Double 89 | public var dimensions: [Double] 90 | 91 | public init(x: Double, y: Double, z: Double) { 92 | self.x = x 93 | self.y = y 94 | self.z = z 95 | dimensions = [x, y, z] 96 | } 97 | 98 | public init(values: [Double]) { 99 | self.x = values[0] 100 | self.y = values[1] 101 | self.z = values[2] 102 | dimensions = values 103 | } 104 | 105 | // Implement Equatable 106 | public static func == (lhs: Point3D, rhs: Point3D) -> Bool { 107 | return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z 108 | } 109 | 110 | // Implement CustomStringConvertible 111 | public var description: String { 112 | return "(\(x), \(y), \(z))" 113 | } 114 | } 115 | 116 | let j = Point3D(x: 2.0, y: 1.0, z: 1.0) 117 | let k = Point3D(x: 2.0, y: 2.0, z: 5.0) 118 | j.distance(to: k) 119 | 120 | public final class KMeans { 121 | public final class Cluster { 122 | var points: [PointType] = [PointType]() 123 | var centroid: PointType 124 | init(centroid: PointType) { 125 | self.centroid = centroid 126 | } 127 | } 128 | 129 | private var points: [PointType] 130 | private var clusters: [Cluster] 131 | 132 | private var centroids: [PointType] { 133 | return clusters.map{ $0.centroid } 134 | } 135 | 136 | init(k: UInt, points: [PointType]) { 137 | self.points = points 138 | clusters = [Cluster]() 139 | zscoreNormalize() 140 | for _ in 0.. [Double] { 147 | return points.map{ $0.dimensions[index] } 148 | } 149 | 150 | private func zscoreNormalize() { 151 | for dimension in 0.. PointType { 159 | var randDimensions = [Double]() 160 | for dimension in 0.. [Cluster] { 195 | for iteration in 0..(k: 1, points: [j, k]) 211 | let testClusters = kmeansTest.run() 212 | for (index, cluster) in testClusters.enumerated() { 213 | print("Cluster \(index): \(cluster.points)") 214 | } 215 | 216 | 217 | struct Governor: DataPoint { 218 | public static let numDimensions: UInt = 2 219 | public let longitude: Double 220 | public let age: Double 221 | public var dimensions: [Double] 222 | public let state: String 223 | 224 | public init(longitude: Double, age: Double, state: String) { 225 | self.longitude = longitude 226 | self.age = age 227 | self.state = state 228 | dimensions = [longitude, age] 229 | } 230 | 231 | public init(values: [Double]) { 232 | self.longitude = values[0] 233 | self.age = values[1] 234 | self.state = "" 235 | dimensions = values 236 | } 237 | 238 | // Implement Equatable 239 | public static func == (lhs: Governor, rhs: Governor) -> Bool { 240 | return lhs.longitude == rhs.longitude && lhs.age == rhs.age && lhs.state == rhs.state 241 | } 242 | 243 | // Implement CustomStringConvertible 244 | public var description: String { 245 | return "\(state): (longitude: \(longitude), age: \(age))" 246 | } 247 | } 248 | 249 | let governors = [Governor(longitude: -86.79113, age: 72, state: "Alabama"), Governor(longitude: -152.404419, age: 66, state: "Alaska"), Governor(longitude: -111.431221, age: 53, state: "Arizona"), Governor(longitude: -92.373123, age: 66, state: "Arkansas"), Governor(longitude: -119.681564, age: 79, state: "California"), Governor(longitude: -105.311104, age: 65, state: "Colorado"), Governor(longitude: -72.755371, age: 61, state: "Connecticut"), Governor(longitude: -75.507141, age: 61, state: "Delaware"), Governor(longitude: -81.686783, age: 64, state: "Florida"), Governor(longitude: -83.643074, age: 74, state: "Georgia"), Governor(longitude: -157.498337, age: 60, state: "Hawaii"), Governor(longitude: -114.478828, age: 75, state: "Idaho"), Governor(longitude: -88.986137, age: 60, state: "Illinois"), Governor(longitude: -86.258278, age: 49, state: "Indiana"), Governor(longitude: -93.210526, age: 57, state: "Iowa"), Governor(longitude: -96.726486, age: 60, state: "Kansas"), Governor(longitude: -84.670067, age: 50, state: "Kentucky"), Governor(longitude: -91.867805, age: 50, state: "Louisiana"), Governor(longitude: -69.381927, age: 68, state: "Maine"), Governor(longitude: -76.802101, age: 61, state: "Maryland"), Governor(longitude: -71.530106, age: 60, state: "Massachusetts"), Governor(longitude: -84.536095, age: 58, state: "Michigan"), Governor(longitude: -93.900192, age: 70, state: "Minnesota"), Governor(longitude: -89.678696, age: 62, state: "Mississippi"), Governor(longitude: -92.288368, age: 43, state: "Missouri"), Governor(longitude: -110.454353, age: 51, state: "Montana"), Governor(longitude: -98.268082, age: 52, state: "Nebraska"), Governor(longitude: -117.055374, age: 53, state: "Nevada"), Governor(longitude: -71.563896, age: 42, state: "New Hampshire"), Governor(longitude: -74.521011, age: 54, state: "New Jersey"), Governor(longitude: -106.248482, age: 57, state: "New Mexico"), Governor(longitude: -74.948051, age: 59, state: "New York"), Governor(longitude: -79.806419, age: 60, state: "North Carolina"), Governor(longitude: -99.784012, age: 60, state: "North Dakota"), Governor(longitude: -82.764915, age: 65, state: "Ohio"), Governor(longitude: -96.928917, age: 62, state: "Oklahoma"), Governor(longitude: -122.070938, age: 56, state: "Oregon"), Governor(longitude: -77.209755, age: 68, state: "Pennsylvania"), Governor(longitude: -71.51178, age: 46, state: "Rhode Island"), Governor(longitude: -80.945007, age: 70, state: "South Carolina"), Governor(longitude: -99.438828, age: 64, state: "South Dakota"), Governor(longitude: -86.692345, age: 58, state: "Tennessee"), Governor(longitude: -97.563461, age: 59, state: "Texas"), Governor(longitude: -111.862434, age: 70, state: "Utah"), Governor(longitude: -72.710686, age: 58, state: "Vermont"), Governor(longitude: -78.169968, age: 60, state: "Virginia"), Governor(longitude: -121.490494, age: 66, state: "Washington"), Governor(longitude: -80.954453, age: 66, state: "West Virginia"), Governor(longitude: -89.616508, age: 49, state: "Wisconsin"), Governor(longitude: -107.30249, age: 55, state: "Wyoming")] 250 | 251 | let kmeans = KMeans(k: 2, points: governors) 252 | let govClusters = kmeans.run() 253 | for (index, cluster) in govClusters.enumerated() { 254 | print("Cluster \(index): \(cluster.points)") 255 | } 256 | 257 | //: [Next](@next) 258 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Pages/Chapter 7.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | // Classic Computer Science Problems in Swift Chapter 7 Source 4 | 5 | // Copyright 2017 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | /// Fairly Simple Neural Networks 20 | 21 | import Accelerate 22 | import Foundation 23 | 24 | // MARK: Randomization & Statistical Helpers 25 | 26 | // A derivative of the Fisher-Yates algorithm to shuffle an array 27 | extension Array { 28 | public func shuffled() -> Array { 29 | var shuffledArray = self // value semantics (Array is Struct) makes this a copy 30 | if count < 2 { return shuffledArray } // already shuffled 31 | for i in (1.. Double { 46 | if !Random.seeded { 47 | srand48(time(nil)) 48 | Random.seeded = true 49 | } 50 | 51 | return (drand48() * (to - from)) + from 52 | } 53 | } 54 | 55 | /// Create *number* of random Doubles between 0.0 and 1.0 56 | func randomWeights(number: Int) -> [Double] { 57 | return (0.. Double { 68 | var answer: Double = 0.0 69 | vDSP_dotprD(xs, 1, ys, 1, &answer, vDSP_Length(xs.count)) 70 | return answer 71 | } 72 | 73 | /// Subtract one vector from another 74 | public func sub(_ x: [Double], _ y: [Double]) -> [Double] { 75 | var results = [Double](y) 76 | catlas_daxpby(Int32(x.count), 1.0, x, 1, -1, &results, 1) 77 | return results 78 | } 79 | 80 | /// Multiply two vectors together 81 | public func mul(_ x: [Double], _ y: [Double]) -> [Double] { 82 | var results = [Double](repeating: 0.0, count: x.count) 83 | vDSP_vmulD(x, 1, y, 1, &results, 1, vDSP_Length(x.count)) 84 | return results 85 | } 86 | 87 | /// Sum a vector 88 | public func sum(_ x: [Double]) -> Double { 89 | var result: Double = 0.0 90 | vDSP_sveD(x, 1, &result, vDSP_Length(x.count)) 91 | return result 92 | } 93 | 94 | // MARK: Activation Functions and Their Derivatives 95 | 96 | /// the classic sigmoid activation function 97 | func sigmoid(_ x: Double) -> Double { 98 | return 1.0 / (1.0 + exp(-x)) 99 | } 100 | 101 | // as derived at http://www.ai.mit.edu/courses/6.892/lecture8-html/sld015.htm 102 | func derivativeSigmoid(_ x: Double) -> Double { 103 | return sigmoid(x) * (1 - sigmoid(x)) 104 | } 105 | 106 | /// An individual node in a layer 107 | class Neuron { 108 | var weights: [Double] 109 | var activationFunction: (Double) -> Double 110 | var derivativeActivationFunction: (Double) -> Double 111 | var outputCache: Double = 0.0 112 | var delta: Double = 0.0 113 | var learningRate: Double 114 | 115 | init(weights: [Double], activationFunction: @escaping (Double) -> Double, derivativeActivationFunction: @escaping (Double) -> Double, learningRate: Double) { 116 | self.weights = weights 117 | self.activationFunction = activationFunction 118 | self.derivativeActivationFunction = derivativeActivationFunction 119 | self.learningRate = learningRate 120 | } 121 | 122 | /// The output that will be going to the next layer 123 | /// or the final output if this is an output layer 124 | func output(inputs: [Double]) -> Double { 125 | outputCache = dotProduct(inputs, weights) 126 | return activationFunction(outputCache) 127 | } 128 | 129 | } 130 | 131 | class Layer { 132 | let previousLayer: Layer? 133 | var neurons: [Neuron] 134 | var outputCache: [Double] 135 | 136 | init(previousLayer: Layer? = nil, numNeurons: Int, activationFunction: @escaping (Double) -> Double, derivativeActivationFunction: @escaping (Double) -> Double, learningRate: Double) { 137 | self.previousLayer = previousLayer 138 | self.neurons = Array() 139 | for _ in 0..(repeating: 0.0, count: neurons.count) 143 | } 144 | 145 | func outputs(inputs: [Double]) -> [Double] { 146 | if previousLayer == nil { // input layer (first layer) 147 | outputCache = inputs 148 | } else { // hidden layer or output layer 149 | outputCache = neurons.map { $0.output(inputs: inputs) } 150 | } 151 | return outputCache 152 | } 153 | 154 | // should only be called on an output layer 155 | func calculateDeltasForOutputLayer(expected: [Double]) { 156 | for n in 0.. Layers -> Neurons 174 | class Network { 175 | var layers: [Layer] 176 | 177 | init(layerStructure:[Int], activationFunction: @escaping (Double) -> Double = sigmoid, derivativeActivationFunction: @escaping (Double) -> Double = derivativeSigmoid, learningRate: Double) { 178 | if (layerStructure.count < 3) { 179 | print("Error: Should be at least 3 layers (1 input, 1 hidden, 1 output)") 180 | } 181 | layers = [Layer]() 182 | // input layer 183 | layers.append(Layer(numNeurons: layerStructure[0], activationFunction: activationFunction, derivativeActivationFunction: derivativeActivationFunction, learningRate: learningRate)) 184 | 185 | // hidden layers and output layer 186 | for x in layerStructure.enumerated() where x.offset != 0 { 187 | layers.append(Layer(previousLayer: layers[x.offset - 1], numNeurons: x.element, activationFunction: activationFunction, derivativeActivationFunction: derivativeActivationFunction, learningRate: learningRate)) 188 | } 189 | } 190 | 191 | /// pushes input data to the first layer 192 | /// then output from the first as input to the second 193 | /// second to the third, etc. 194 | func outputs(input: [Double]) -> [Double] { 195 | return layers.reduce(input) { $1.outputs(inputs: $0) } 196 | } 197 | 198 | /// Figure out each neuron's changes based on the errors 199 | /// of the output versus the expected outcome 200 | func backpropagate(expected: [Double]) { 201 | //calculate delta for output layer neurons 202 | layers.last?.calculateDeltasForOutputLayer(expected: expected) 203 | //calculate delta for prior layers 204 | for l in (1..(inputs:[[Double]], expecteds:[T], interpretOutput: ([Double]) -> T) -> (correct: Int, total: Int, percentage: Double) { 243 | var correct = 0 244 | for (input, expected) in zip(inputs, expecteds) { 245 | let result = interpretOutput(outputs(input: input)) 246 | if result == expected { 247 | correct += 1 248 | } 249 | } 250 | let percentage = Double(correct) / Double(inputs.count) 251 | return (correct, inputs.count, percentage) 252 | } 253 | } 254 | 255 | /// MARK: Normalization 256 | 257 | /// assumes all rows are of equal length 258 | /// and feature scale each column to be in the range 0 – 1 259 | func normalizeByFeatureScaling(dataset: inout [[Double]]) { 260 | for colNum in 0.. (parameters: [[Double]], classifications: [[Double]], species: [String]) { 273 | // let urlpath = Bundle.main.path(forResource: "iris", ofType: "csv") 274 | // let url = URL(fileURLWithPath: urlpath!) 275 | // let csv = try! String.init(contentsOf: url) 276 | // let lines = csv.components(separatedBy: "\n") 277 | // var irisParameters: [[Double]] = [[Double]]() 278 | // var irisClassifications: [[Double]] = [[Double]]() 279 | // var irisSpecies: [String] = [String]() 280 | // 281 | // let shuffledLines = lines.shuffled() 282 | // for line in shuffledLines { 283 | // if line == "" { continue } // skip blank lines 284 | // let items = line.components(separatedBy: ",") 285 | // let parameters = items[0...3].map{ Double($0)! } 286 | // irisParameters.append(parameters) 287 | // let species = items[4] 288 | // if species == "Iris-setosa" { 289 | // irisClassifications.append([1.0, 0.0, 0.0]) 290 | // } else if species == "Iris-versicolor" { 291 | // irisClassifications.append([0.0, 1.0, 0.0]) 292 | // } else { 293 | // irisClassifications.append([0.0, 0.0, 1.0]) 294 | // } 295 | // irisSpecies.append(species) 296 | // } 297 | // normalizeByFeatureScaling(dataset: &irisParameters) 298 | // return (irisParameters, irisClassifications, irisSpecies) 299 | //} 300 | // 301 | //let (irisParameters, irisClassifications, irisSpecies) = parseIrisCSV() 302 | // 303 | //let irisNetwork: Network = Network(layerStructure: [4, 6, 3], learningRate: 0.3) 304 | // 305 | //func irisInterpretOutput(output: [Double]) -> String { 306 | // if output.max()! == output[0] { 307 | // return "Iris-setosa" 308 | // } else if output.max()! == output[1] { 309 | // return "Iris-versicolor" 310 | // } else { 311 | // return "Iris-virginica" 312 | // } 313 | //} 314 | // 315 | //// train over first 140 irises in data set 20 times 316 | //let irisTrainers = Array(irisParameters[0..<140]) 317 | //let irisTrainersCorrects = Array(irisClassifications[0..<140]) 318 | //for _ in 0..<20 { 319 | // irisNetwork.train(inputs: irisTrainers, expecteds: irisTrainersCorrects, printError: false) 320 | //} 321 | // 322 | //// test over the last 10 of the irises in the data set 323 | //let irisTesters = Array(irisParameters[140..<150]) 324 | //let irisTestersCorrects = Array(irisSpecies[140..<150]) 325 | //let irisResults = irisNetwork.validate(inputs: irisTesters, expecteds: irisTestersCorrects, interpretOutput: irisInterpretOutput) 326 | //print("\(irisResults.correct) correct of \(irisResults.total) = \(irisResults.percentage * 100)%") 327 | 328 | /// Wine Test 329 | 330 | func parseWineCSV() -> (parameters: [[Double]], classifications: [[Double]], species: [Int]) { 331 | let urlpath = Bundle.main.path(forResource: "wine", ofType: "csv") 332 | let url = URL(fileURLWithPath: urlpath!) 333 | let csv = try! String.init(contentsOf: url) 334 | let lines = csv.components(separatedBy: "\n") 335 | var wineParameters: [[Double]] = [[Double]]() 336 | var wineClassifications: [[Double]] = [[Double]]() 337 | var wineSpecies: [Int] = [Int]() 338 | 339 | let shuffledLines = lines.shuffled() 340 | for line in shuffledLines { 341 | if line == "" { continue } // skip blank lines 342 | let items = line.components(separatedBy: ",") 343 | let parameters = items[1...13].map{ Double($0)! } 344 | wineParameters.append(parameters) 345 | let species = Int(items[0])! 346 | if species == 1 { 347 | wineClassifications.append([1.0, 0.0, 0.0]) 348 | } else if species == 2 { 349 | wineClassifications.append([0.0, 1.0, 0.0]) 350 | } else { 351 | wineClassifications.append([0.0, 0.0, 1.0]) 352 | } 353 | wineSpecies.append(species) 354 | } 355 | normalizeByFeatureScaling(dataset: &wineParameters) 356 | return (wineParameters, wineClassifications, wineSpecies) 357 | } 358 | 359 | let (wineParameters, wineClassifications, wineSpecies) = parseWineCSV() 360 | 361 | let wineNetwork: Network = Network(layerStructure: [13, 7, 3], learningRate: 0.9) 362 | 363 | func wineInterpretOutput(output: [Double]) -> Int { 364 | if output.max()! == output[0] { 365 | return 1 366 | } else if output.max()! == output[1] { 367 | return 2 368 | } else { 369 | return 3 370 | } 371 | } 372 | 373 | // train over the first 150 samples 5 times 374 | let wineTrainers = Array(wineParameters.dropLast(28)) 375 | let wineTrainersCorrects = Array(wineClassifications.dropLast(28)) 376 | for _ in 0..<5 { 377 | wineNetwork.train(inputs: wineTrainers, expecteds: wineTrainersCorrects, printError: false) 378 | } 379 | 380 | let wineTesters = Array(wineParameters.dropFirst(150)) 381 | let wineTestersCorrects = Array(wineSpecies.dropFirst(150)) 382 | let results = wineNetwork.validate(inputs: wineTesters, expecteds: wineTestersCorrects, interpretOutput: wineInterpretOutput) 383 | print("\(results.correct) correct of \(results.total) = \(results.percentage * 100)%") 384 | 385 | //: [Next](@next) 386 | 387 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Pages/Chapter 8.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | // Classic Computer Science Problems in Swift Chapter 8 Source 4 | 5 | // Copyright 2017 David Kopec 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | /// Knapsack 22 | 23 | struct Item { 24 | let name: String 25 | let weight: Int 26 | let value: Float 27 | } 28 | 29 | // originally based on Algorithms in C by Sedgewick, Second Edition, p 596 30 | func knapsack(items: [Item], maxCapacity: Int) -> [Item] { 31 | //build up dynamic programming table 32 | var table: [[Float]] = [[Float]](repeating: [Float](repeating: 0.0, count: maxCapacity + 1), count: items.count + 1) //initialize table - overshooting in size 33 | for (i, item) in items.enumerated() { 34 | for capacity in 1...maxCapacity { 35 | let previousItemsValue = table[i][capacity] 36 | if capacity >= item.weight { // item fits in knapsack 37 | let valueFreeingWeightForItem = table[i][capacity - item.weight] 38 | table[i + 1][capacity] = max(valueFreeingWeightForItem + item.value, previousItemsValue) // only take if more valuable than previous combo 39 | } else { // no room for this item 40 | table[i + 1][capacity] = previousItemsValue //use prior combo 41 | } 42 | } 43 | } 44 | // figure out solution from table 45 | var solution: [Item] = [Item]() 46 | var capacity = maxCapacity 47 | for i in stride(from: items.count, to: 0, by: -1) { // work backwards 48 | if table[i - 1][capacity] != table[i][capacity] { // did we use this item? 49 | solution.append(items[i - 1]) 50 | capacity -= items[i - 1].weight // if we used an item, remove its weight 51 | } 52 | } 53 | return solution 54 | } 55 | 56 | let items = [Item(name: "television", weight: 50, value: 500), 57 | Item(name: "candlesticks", weight: 2, value: 300), 58 | Item(name: "stereo", weight: 35, value: 400), 59 | Item(name: "laptop", weight: 3, value: 1000), 60 | Item(name: "food", weight: 15, value: 50), 61 | Item(name: "clothing", weight: 20, value: 800), 62 | Item(name: "jewelry", weight: 1, value: 4000), 63 | Item(name: "books", weight: 100, value: 300), 64 | Item(name: "printer", weight: 18, value: 30), 65 | Item(name: "refrigerator", weight: 200, value: 700), 66 | Item(name: "painting", weight: 10, value: 1000)] 67 | knapsack(items: items, maxCapacity: 75) 68 | 69 | /// Travelling Salesman 70 | 71 | let vtCities = ["Rutland", "Burlington", "White River Junction", "Bennington", "Brattleboro"] 72 | 73 | let vtDistances = [ 74 | "Rutland": 75 | ["Burlington": 67, "White River Junction": 46, "Bennington": 55, "Brattleboro": 75], 76 | "Burlington": 77 | ["Rutland": 67, "White River Junction": 91, "Bennington": 122, "Brattleboro": 153], 78 | "White River Junction": 79 | ["Rutland": 46, "Burlington": 91, "Bennington": 98, "Brattleboro": 65], 80 | "Bennington": 81 | ["Rutland": 55, "Burlington": 122, "White River Junction": 98, "Brattleboro": 40], 82 | "Brattleboro": 83 | ["Rutland": 75, "Burlington": 153, "White River Junction": 65, "Bennington": 40] 84 | ] 85 | 86 | // backtracking permutations algorithm 87 | func allPermutationsHelper(contents: [T], permutations: inout [[T]], n: Int) { 88 | guard n > 0 else { permutations.append(contents); return } 89 | var tempContents = contents 90 | for i in 0..(_ original: [T]) -> [[T]] { 100 | var permutations = [[T]]() 101 | allPermutationsHelper(contents: original, permutations: &permutations, n: original.count) 102 | return permutations 103 | } 104 | 105 | // test allPermutations 106 | let abc = ["a","b","c"] 107 | let testPerms = allPermutations(abc) 108 | print(testPerms) 109 | print(testPerms.count) 110 | 111 | // make complete paths for tsp 112 | func tspPaths(_ permutations: [[T]]) -> [[T]] { 113 | return permutations.map { 114 | if let first = $0.first { 115 | return ($0 + [first]) // append first to end 116 | } else { 117 | return [] // empty is just itself 118 | } 119 | } 120 | } 121 | 122 | print(tspPaths(testPerms)) 123 | 124 | func solveTSP(cities: [T], distances: [T: [T: Int]]) -> (solution: [T], distance: Int) { 125 | let possiblePaths = tspPaths(allPermutations(cities)) // all potential paths 126 | var bestPath: [T] = [] // shortest path by distance 127 | var minDistance: Int = Int.max // distance of the shortest path 128 | for path in possiblePaths { 129 | if path.count < 2 { continue } // must be at least one city pair to calculate 130 | var distance = 0 131 | var last = path.first! // we know there is one becuase of above line 132 | for next in path[1.. [[Character]]{ 154 | let possibilities = s.compactMap{ mapping[$0] } 155 | print(possibilities) 156 | return combineAllPossibilities(possibilities) 157 | } 158 | 159 | // takes a set of possible characters for each position and finds all possible permutations 160 | func combineAllPossibilities(_ possibilities: [[Character]]) -> [[Character]] { 161 | guard let possibility = possibilities.first else { return [[]] } 162 | var permutations: [[Character]] = possibility.map { [$0] } // turn each into an array 163 | for possibility in possibilities[1.. Board { 216 | var tempPosition = position 217 | tempPosition[location] = turn 218 | return Board(position: tempPosition, turn: turn.opposite, lastMove: location) 219 | } 220 | 221 | // the legal moves in a position are all of the empty squares 222 | var legalMoves: [Move] { 223 | return position.indices.filter { position[$0] == .E } 224 | } 225 | 226 | var isWin: Bool { 227 | return 228 | position[0] == position[1] && position[0] == position[2] && position[0] != .E || // row 0 229 | position[3] == position[4] && position[3] == position[5] && position[3] != .E || // row 1 230 | position[6] == position[7] && position[6] == position[8] && position[6] != .E || // row 2 231 | position[0] == position[3] && position[0] == position[6] && position[0] != .E || // col 0 232 | position[1] == position[4] && position[1] == position[7] && position[1] != .E || // col 1 233 | position[2] == position[5] && position[2] == position[8] && position[2] != .E || // col 2 234 | position[0] == position[4] && position[0] == position[8] && position[0] != .E || // diag 0 235 | position[2] == position[4] && position[2] == position[6] && position[2] != .E // diag 1 236 | 237 | } 238 | 239 | var isDraw: Bool { 240 | return !isWin && legalMoves.count == 0 241 | } 242 | } 243 | 244 | // Find the best possible outcome for originalPlayer 245 | func minimax(_ board: Board, maximizing: Bool, originalPlayer: Piece) -> Int { 246 | // Base case — evaluate the position if it is a win or a draw 247 | if board.isWin && originalPlayer == board.turn.opposite { return 1 } // win 248 | else if board.isWin && originalPlayer != board.turn.opposite { return -1 } // loss 249 | else if board.isDraw { return 0 } // draw 250 | 251 | // Recursive case — maximize your gains or minimize the opponent's gains 252 | if maximizing { 253 | var bestEval = Int.min 254 | for move in board.legalMoves { // find the move with the highest evaluation 255 | let result = minimax(board.move(move), maximizing: false, originalPlayer: originalPlayer) 256 | bestEval = max(result, bestEval) 257 | } 258 | return bestEval 259 | } else { // minimizing 260 | var worstEval = Int.max 261 | for move in board.legalMoves { 262 | let result = minimax(board.move(move), maximizing: true, originalPlayer: originalPlayer) 263 | worstEval = min(result, worstEval) 264 | } 265 | return worstEval 266 | } 267 | } 268 | 269 | // Run minimax on every possible move to find the best one 270 | func findBestMove(_ board: Board) -> Move { 271 | var bestEval = Int.min 272 | var bestMove = -1 273 | for move in board.legalMoves { 274 | let result = minimax(board.move(move), maximizing: false, originalPlayer: board.turn) 275 | if result > bestEval { 276 | bestEval = result 277 | bestMove = move 278 | } 279 | } 280 | return bestMove 281 | } 282 | 283 | // win in 1 move 284 | let toWinEasyPosition: [Piece] = [.X, .O, .X, 285 | .X, .E, .O, 286 | .E, .E, .O] 287 | let testBoard1: Board = Board(position: toWinEasyPosition, turn: .X, lastMove: 8) 288 | let answer1 = findBestMove(testBoard1) 289 | print(answer1) 290 | 291 | // must block O's win 292 | let toBlockPosition: [Piece] = [.X, .E, .E, 293 | .E, .E, .O, 294 | .E, .X, .O] 295 | let testBoard2: Board = Board(position: toBlockPosition, turn: .X, lastMove: 8) 296 | let answer2 = findBestMove(testBoard2) 297 | print(answer2) 298 | 299 | // find the best move to win in 2 moves 300 | let toWinHardPosition: [Piece] = [.X, .E, .E, 301 | .E, .E, .O, 302 | .O, .X, .E] 303 | let testBoard3: Board = Board(position: toWinHardPosition, turn: .X, lastMove: 6) 304 | let answer3 = findBestMove(testBoard3) 305 | print(answer3) 306 | //: [Next](@next) 307 | 308 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Resources/iris.csv: -------------------------------------------------------------------------------- 1 | 5.1,3.5,1.4,0.2,Iris-setosa 2 | 4.9,3.0,1.4,0.2,Iris-setosa 3 | 4.7,3.2,1.3,0.2,Iris-setosa 4 | 4.6,3.1,1.5,0.2,Iris-setosa 5 | 5.0,3.6,1.4,0.2,Iris-setosa 6 | 5.4,3.9,1.7,0.4,Iris-setosa 7 | 4.6,3.4,1.4,0.3,Iris-setosa 8 | 5.0,3.4,1.5,0.2,Iris-setosa 9 | 4.4,2.9,1.4,0.2,Iris-setosa 10 | 4.9,3.1,1.5,0.1,Iris-setosa 11 | 5.4,3.7,1.5,0.2,Iris-setosa 12 | 4.8,3.4,1.6,0.2,Iris-setosa 13 | 4.8,3.0,1.4,0.1,Iris-setosa 14 | 4.3,3.0,1.1,0.1,Iris-setosa 15 | 5.8,4.0,1.2,0.2,Iris-setosa 16 | 5.7,4.4,1.5,0.4,Iris-setosa 17 | 5.4,3.9,1.3,0.4,Iris-setosa 18 | 5.1,3.5,1.4,0.3,Iris-setosa 19 | 5.7,3.8,1.7,0.3,Iris-setosa 20 | 5.1,3.8,1.5,0.3,Iris-setosa 21 | 5.4,3.4,1.7,0.2,Iris-setosa 22 | 5.1,3.7,1.5,0.4,Iris-setosa 23 | 4.6,3.6,1.0,0.2,Iris-setosa 24 | 5.1,3.3,1.7,0.5,Iris-setosa 25 | 4.8,3.4,1.9,0.2,Iris-setosa 26 | 5.0,3.0,1.6,0.2,Iris-setosa 27 | 5.0,3.4,1.6,0.4,Iris-setosa 28 | 5.2,3.5,1.5,0.2,Iris-setosa 29 | 5.2,3.4,1.4,0.2,Iris-setosa 30 | 4.7,3.2,1.6,0.2,Iris-setosa 31 | 4.8,3.1,1.6,0.2,Iris-setosa 32 | 5.4,3.4,1.5,0.4,Iris-setosa 33 | 5.2,4.1,1.5,0.1,Iris-setosa 34 | 5.5,4.2,1.4,0.2,Iris-setosa 35 | 4.9,3.1,1.5,0.2,Iris-setosa 36 | 5.0,3.2,1.2,0.2,Iris-setosa 37 | 5.5,3.5,1.3,0.2,Iris-setosa 38 | 4.9,3.6,1.4,0.1,Iris-setosa 39 | 4.4,3.0,1.3,0.2,Iris-setosa 40 | 5.1,3.4,1.5,0.2,Iris-setosa 41 | 5.0,3.5,1.3,0.3,Iris-setosa 42 | 4.5,2.3,1.3,0.3,Iris-setosa 43 | 4.4,3.2,1.3,0.2,Iris-setosa 44 | 5.0,3.5,1.6,0.6,Iris-setosa 45 | 5.1,3.8,1.9,0.4,Iris-setosa 46 | 4.8,3.0,1.4,0.3,Iris-setosa 47 | 5.1,3.8,1.6,0.2,Iris-setosa 48 | 4.6,3.2,1.4,0.2,Iris-setosa 49 | 5.3,3.7,1.5,0.2,Iris-setosa 50 | 5.0,3.3,1.4,0.2,Iris-setosa 51 | 7.0,3.2,4.7,1.4,Iris-versicolor 52 | 6.4,3.2,4.5,1.5,Iris-versicolor 53 | 6.9,3.1,4.9,1.5,Iris-versicolor 54 | 5.5,2.3,4.0,1.3,Iris-versicolor 55 | 6.5,2.8,4.6,1.5,Iris-versicolor 56 | 5.7,2.8,4.5,1.3,Iris-versicolor 57 | 6.3,3.3,4.7,1.6,Iris-versicolor 58 | 4.9,2.4,3.3,1.0,Iris-versicolor 59 | 6.6,2.9,4.6,1.3,Iris-versicolor 60 | 5.2,2.7,3.9,1.4,Iris-versicolor 61 | 5.0,2.0,3.5,1.0,Iris-versicolor 62 | 5.9,3.0,4.2,1.5,Iris-versicolor 63 | 6.0,2.2,4.0,1.0,Iris-versicolor 64 | 6.1,2.9,4.7,1.4,Iris-versicolor 65 | 5.6,2.9,3.6,1.3,Iris-versicolor 66 | 6.7,3.1,4.4,1.4,Iris-versicolor 67 | 5.6,3.0,4.5,1.5,Iris-versicolor 68 | 5.8,2.7,4.1,1.0,Iris-versicolor 69 | 6.2,2.2,4.5,1.5,Iris-versicolor 70 | 5.6,2.5,3.9,1.1,Iris-versicolor 71 | 5.9,3.2,4.8,1.8,Iris-versicolor 72 | 6.1,2.8,4.0,1.3,Iris-versicolor 73 | 6.3,2.5,4.9,1.5,Iris-versicolor 74 | 6.1,2.8,4.7,1.2,Iris-versicolor 75 | 6.4,2.9,4.3,1.3,Iris-versicolor 76 | 6.6,3.0,4.4,1.4,Iris-versicolor 77 | 6.8,2.8,4.8,1.4,Iris-versicolor 78 | 6.7,3.0,5.0,1.7,Iris-versicolor 79 | 6.0,2.9,4.5,1.5,Iris-versicolor 80 | 5.7,2.6,3.5,1.0,Iris-versicolor 81 | 5.5,2.4,3.8,1.1,Iris-versicolor 82 | 5.5,2.4,3.7,1.0,Iris-versicolor 83 | 5.8,2.7,3.9,1.2,Iris-versicolor 84 | 6.0,2.7,5.1,1.6,Iris-versicolor 85 | 5.4,3.0,4.5,1.5,Iris-versicolor 86 | 6.0,3.4,4.5,1.6,Iris-versicolor 87 | 6.7,3.1,4.7,1.5,Iris-versicolor 88 | 6.3,2.3,4.4,1.3,Iris-versicolor 89 | 5.6,3.0,4.1,1.3,Iris-versicolor 90 | 5.5,2.5,4.0,1.3,Iris-versicolor 91 | 5.5,2.6,4.4,1.2,Iris-versicolor 92 | 6.1,3.0,4.6,1.4,Iris-versicolor 93 | 5.8,2.6,4.0,1.2,Iris-versicolor 94 | 5.0,2.3,3.3,1.0,Iris-versicolor 95 | 5.6,2.7,4.2,1.3,Iris-versicolor 96 | 5.7,3.0,4.2,1.2,Iris-versicolor 97 | 5.7,2.9,4.2,1.3,Iris-versicolor 98 | 6.2,2.9,4.3,1.3,Iris-versicolor 99 | 5.1,2.5,3.0,1.1,Iris-versicolor 100 | 5.7,2.8,4.1,1.3,Iris-versicolor 101 | 6.3,3.3,6.0,2.5,Iris-virginica 102 | 5.8,2.7,5.1,1.9,Iris-virginica 103 | 7.1,3.0,5.9,2.1,Iris-virginica 104 | 6.3,2.9,5.6,1.8,Iris-virginica 105 | 6.5,3.0,5.8,2.2,Iris-virginica 106 | 7.6,3.0,6.6,2.1,Iris-virginica 107 | 4.9,2.5,4.5,1.7,Iris-virginica 108 | 7.3,2.9,6.3,1.8,Iris-virginica 109 | 6.7,2.5,5.8,1.8,Iris-virginica 110 | 7.2,3.6,6.1,2.5,Iris-virginica 111 | 6.5,3.2,5.1,2.0,Iris-virginica 112 | 6.4,2.7,5.3,1.9,Iris-virginica 113 | 6.8,3.0,5.5,2.1,Iris-virginica 114 | 5.7,2.5,5.0,2.0,Iris-virginica 115 | 5.8,2.8,5.1,2.4,Iris-virginica 116 | 6.4,3.2,5.3,2.3,Iris-virginica 117 | 6.5,3.0,5.5,1.8,Iris-virginica 118 | 7.7,3.8,6.7,2.2,Iris-virginica 119 | 7.7,2.6,6.9,2.3,Iris-virginica 120 | 6.0,2.2,5.0,1.5,Iris-virginica 121 | 6.9,3.2,5.7,2.3,Iris-virginica 122 | 5.6,2.8,4.9,2.0,Iris-virginica 123 | 7.7,2.8,6.7,2.0,Iris-virginica 124 | 6.3,2.7,4.9,1.8,Iris-virginica 125 | 6.7,3.3,5.7,2.1,Iris-virginica 126 | 7.2,3.2,6.0,1.8,Iris-virginica 127 | 6.2,2.8,4.8,1.8,Iris-virginica 128 | 6.1,3.0,4.9,1.8,Iris-virginica 129 | 6.4,2.8,5.6,2.1,Iris-virginica 130 | 7.2,3.0,5.8,1.6,Iris-virginica 131 | 7.4,2.8,6.1,1.9,Iris-virginica 132 | 7.9,3.8,6.4,2.0,Iris-virginica 133 | 6.4,2.8,5.6,2.2,Iris-virginica 134 | 6.3,2.8,5.1,1.5,Iris-virginica 135 | 6.1,2.6,5.6,1.4,Iris-virginica 136 | 7.7,3.0,6.1,2.3,Iris-virginica 137 | 6.3,3.4,5.6,2.4,Iris-virginica 138 | 6.4,3.1,5.5,1.8,Iris-virginica 139 | 6.0,3.0,4.8,1.8,Iris-virginica 140 | 6.9,3.1,5.4,2.1,Iris-virginica 141 | 6.7,3.1,5.6,2.4,Iris-virginica 142 | 6.9,3.1,5.1,2.3,Iris-virginica 143 | 5.8,2.7,5.1,1.9,Iris-virginica 144 | 6.8,3.2,5.9,2.3,Iris-virginica 145 | 6.7,3.3,5.7,2.5,Iris-virginica 146 | 6.7,3.0,5.2,2.3,Iris-virginica 147 | 6.3,2.5,5.0,1.9,Iris-virginica 148 | 6.5,3.0,5.2,2.0,Iris-virginica 149 | 6.2,3.4,5.4,2.3,Iris-virginica 150 | 5.9,3.0,5.1,1.8,Iris-virginica 151 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Resources/wine.csv: -------------------------------------------------------------------------------- 1 | 1,14.23,1.71,2.43,15.6,127,2.8,3.06,.28,2.29,5.64,1.04,3.92,1065 2 | 1,13.2,1.78,2.14,11.2,100,2.65,2.76,.26,1.28,4.38,1.05,3.4,1050 3 | 1,13.16,2.36,2.67,18.6,101,2.8,3.24,.3,2.81,5.68,1.03,3.17,1185 4 | 1,14.37,1.95,2.5,16.8,113,3.85,3.49,.24,2.18,7.8,.86,3.45,1480 5 | 1,13.24,2.59,2.87,21,118,2.8,2.69,.39,1.82,4.32,1.04,2.93,735 6 | 1,14.2,1.76,2.45,15.2,112,3.27,3.39,.34,1.97,6.75,1.05,2.85,1450 7 | 1,14.39,1.87,2.45,14.6,96,2.5,2.52,.3,1.98,5.25,1.02,3.58,1290 8 | 1,14.06,2.15,2.61,17.6,121,2.6,2.51,.31,1.25,5.05,1.06,3.58,1295 9 | 1,14.83,1.64,2.17,14,97,2.8,2.98,.29,1.98,5.2,1.08,2.85,1045 10 | 1,13.86,1.35,2.27,16,98,2.98,3.15,.22,1.85,7.22,1.01,3.55,1045 11 | 1,14.1,2.16,2.3,18,105,2.95,3.32,.22,2.38,5.75,1.25,3.17,1510 12 | 1,14.12,1.48,2.32,16.8,95,2.2,2.43,.26,1.57,5,1.17,2.82,1280 13 | 1,13.75,1.73,2.41,16,89,2.6,2.76,.29,1.81,5.6,1.15,2.9,1320 14 | 1,14.75,1.73,2.39,11.4,91,3.1,3.69,.43,2.81,5.4,1.25,2.73,1150 15 | 1,14.38,1.87,2.38,12,102,3.3,3.64,.29,2.96,7.5,1.2,3,1547 16 | 1,13.63,1.81,2.7,17.2,112,2.85,2.91,.3,1.46,7.3,1.28,2.88,1310 17 | 1,14.3,1.92,2.72,20,120,2.8,3.14,.33,1.97,6.2,1.07,2.65,1280 18 | 1,13.83,1.57,2.62,20,115,2.95,3.4,.4,1.72,6.6,1.13,2.57,1130 19 | 1,14.19,1.59,2.48,16.5,108,3.3,3.93,.32,1.86,8.7,1.23,2.82,1680 20 | 1,13.64,3.1,2.56,15.2,116,2.7,3.03,.17,1.66,5.1,.96,3.36,845 21 | 1,14.06,1.63,2.28,16,126,3,3.17,.24,2.1,5.65,1.09,3.71,780 22 | 1,12.93,3.8,2.65,18.6,102,2.41,2.41,.25,1.98,4.5,1.03,3.52,770 23 | 1,13.71,1.86,2.36,16.6,101,2.61,2.88,.27,1.69,3.8,1.11,4,1035 24 | 1,12.85,1.6,2.52,17.8,95,2.48,2.37,.26,1.46,3.93,1.09,3.63,1015 25 | 1,13.5,1.81,2.61,20,96,2.53,2.61,.28,1.66,3.52,1.12,3.82,845 26 | 1,13.05,2.05,3.22,25,124,2.63,2.68,.47,1.92,3.58,1.13,3.2,830 27 | 1,13.39,1.77,2.62,16.1,93,2.85,2.94,.34,1.45,4.8,.92,3.22,1195 28 | 1,13.3,1.72,2.14,17,94,2.4,2.19,.27,1.35,3.95,1.02,2.77,1285 29 | 1,13.87,1.9,2.8,19.4,107,2.95,2.97,.37,1.76,4.5,1.25,3.4,915 30 | 1,14.02,1.68,2.21,16,96,2.65,2.33,.26,1.98,4.7,1.04,3.59,1035 31 | 1,13.73,1.5,2.7,22.5,101,3,3.25,.29,2.38,5.7,1.19,2.71,1285 32 | 1,13.58,1.66,2.36,19.1,106,2.86,3.19,.22,1.95,6.9,1.09,2.88,1515 33 | 1,13.68,1.83,2.36,17.2,104,2.42,2.69,.42,1.97,3.84,1.23,2.87,990 34 | 1,13.76,1.53,2.7,19.5,132,2.95,2.74,.5,1.35,5.4,1.25,3,1235 35 | 1,13.51,1.8,2.65,19,110,2.35,2.53,.29,1.54,4.2,1.1,2.87,1095 36 | 1,13.48,1.81,2.41,20.5,100,2.7,2.98,.26,1.86,5.1,1.04,3.47,920 37 | 1,13.28,1.64,2.84,15.5,110,2.6,2.68,.34,1.36,4.6,1.09,2.78,880 38 | 1,13.05,1.65,2.55,18,98,2.45,2.43,.29,1.44,4.25,1.12,2.51,1105 39 | 1,13.07,1.5,2.1,15.5,98,2.4,2.64,.28,1.37,3.7,1.18,2.69,1020 40 | 1,14.22,3.99,2.51,13.2,128,3,3.04,.2,2.08,5.1,.89,3.53,760 41 | 1,13.56,1.71,2.31,16.2,117,3.15,3.29,.34,2.34,6.13,.95,3.38,795 42 | 1,13.41,3.84,2.12,18.8,90,2.45,2.68,.27,1.48,4.28,.91,3,1035 43 | 1,13.88,1.89,2.59,15,101,3.25,3.56,.17,1.7,5.43,.88,3.56,1095 44 | 1,13.24,3.98,2.29,17.5,103,2.64,2.63,.32,1.66,4.36,.82,3,680 45 | 1,13.05,1.77,2.1,17,107,3,3,.28,2.03,5.04,.88,3.35,885 46 | 1,14.21,4.04,2.44,18.9,111,2.85,2.65,.3,1.25,5.24,.87,3.33,1080 47 | 1,14.38,3.59,2.28,16,102,3.25,3.17,.27,2.19,4.9,1.04,3.44,1065 48 | 1,13.9,1.68,2.12,16,101,3.1,3.39,.21,2.14,6.1,.91,3.33,985 49 | 1,14.1,2.02,2.4,18.8,103,2.75,2.92,.32,2.38,6.2,1.07,2.75,1060 50 | 1,13.94,1.73,2.27,17.4,108,2.88,3.54,.32,2.08,8.90,1.12,3.1,1260 51 | 1,13.05,1.73,2.04,12.4,92,2.72,3.27,.17,2.91,7.2,1.12,2.91,1150 52 | 1,13.83,1.65,2.6,17.2,94,2.45,2.99,.22,2.29,5.6,1.24,3.37,1265 53 | 1,13.82,1.75,2.42,14,111,3.88,3.74,.32,1.87,7.05,1.01,3.26,1190 54 | 1,13.77,1.9,2.68,17.1,115,3,2.79,.39,1.68,6.3,1.13,2.93,1375 55 | 1,13.74,1.67,2.25,16.4,118,2.6,2.9,.21,1.62,5.85,.92,3.2,1060 56 | 1,13.56,1.73,2.46,20.5,116,2.96,2.78,.2,2.45,6.25,.98,3.03,1120 57 | 1,14.22,1.7,2.3,16.3,118,3.2,3,.26,2.03,6.38,.94,3.31,970 58 | 1,13.29,1.97,2.68,16.8,102,3,3.23,.31,1.66,6,1.07,2.84,1270 59 | 1,13.72,1.43,2.5,16.7,108,3.4,3.67,.19,2.04,6.8,.89,2.87,1285 60 | 2,12.37,.94,1.36,10.6,88,1.98,.57,.28,.42,1.95,1.05,1.82,520 61 | 2,12.33,1.1,2.28,16,101,2.05,1.09,.63,.41,3.27,1.25,1.67,680 62 | 2,12.64,1.36,2.02,16.8,100,2.02,1.41,.53,.62,5.75,.98,1.59,450 63 | 2,13.67,1.25,1.92,18,94,2.1,1.79,.32,.73,3.8,1.23,2.46,630 64 | 2,12.37,1.13,2.16,19,87,3.5,3.1,.19,1.87,4.45,1.22,2.87,420 65 | 2,12.17,1.45,2.53,19,104,1.89,1.75,.45,1.03,2.95,1.45,2.23,355 66 | 2,12.37,1.21,2.56,18.1,98,2.42,2.65,.37,2.08,4.6,1.19,2.3,678 67 | 2,13.11,1.01,1.7,15,78,2.98,3.18,.26,2.28,5.3,1.12,3.18,502 68 | 2,12.37,1.17,1.92,19.6,78,2.11,2,.27,1.04,4.68,1.12,3.48,510 69 | 2,13.34,.94,2.36,17,110,2.53,1.3,.55,.42,3.17,1.02,1.93,750 70 | 2,12.21,1.19,1.75,16.8,151,1.85,1.28,.14,2.5,2.85,1.28,3.07,718 71 | 2,12.29,1.61,2.21,20.4,103,1.1,1.02,.37,1.46,3.05,.906,1.82,870 72 | 2,13.86,1.51,2.67,25,86,2.95,2.86,.21,1.87,3.38,1.36,3.16,410 73 | 2,13.49,1.66,2.24,24,87,1.88,1.84,.27,1.03,3.74,.98,2.78,472 74 | 2,12.99,1.67,2.6,30,139,3.3,2.89,.21,1.96,3.35,1.31,3.5,985 75 | 2,11.96,1.09,2.3,21,101,3.38,2.14,.13,1.65,3.21,.99,3.13,886 76 | 2,11.66,1.88,1.92,16,97,1.61,1.57,.34,1.15,3.8,1.23,2.14,428 77 | 2,13.03,.9,1.71,16,86,1.95,2.03,.24,1.46,4.6,1.19,2.48,392 78 | 2,11.84,2.89,2.23,18,112,1.72,1.32,.43,.95,2.65,.96,2.52,500 79 | 2,12.33,.99,1.95,14.8,136,1.9,1.85,.35,2.76,3.4,1.06,2.31,750 80 | 2,12.7,3.87,2.4,23,101,2.83,2.55,.43,1.95,2.57,1.19,3.13,463 81 | 2,12,.92,2,19,86,2.42,2.26,.3,1.43,2.5,1.38,3.12,278 82 | 2,12.72,1.81,2.2,18.8,86,2.2,2.53,.26,1.77,3.9,1.16,3.14,714 83 | 2,12.08,1.13,2.51,24,78,2,1.58,.4,1.4,2.2,1.31,2.72,630 84 | 2,13.05,3.86,2.32,22.5,85,1.65,1.59,.61,1.62,4.8,.84,2.01,515 85 | 2,11.84,.89,2.58,18,94,2.2,2.21,.22,2.35,3.05,.79,3.08,520 86 | 2,12.67,.98,2.24,18,99,2.2,1.94,.3,1.46,2.62,1.23,3.16,450 87 | 2,12.16,1.61,2.31,22.8,90,1.78,1.69,.43,1.56,2.45,1.33,2.26,495 88 | 2,11.65,1.67,2.62,26,88,1.92,1.61,.4,1.34,2.6,1.36,3.21,562 89 | 2,11.64,2.06,2.46,21.6,84,1.95,1.69,.48,1.35,2.8,1,2.75,680 90 | 2,12.08,1.33,2.3,23.6,70,2.2,1.59,.42,1.38,1.74,1.07,3.21,625 91 | 2,12.08,1.83,2.32,18.5,81,1.6,1.5,.52,1.64,2.4,1.08,2.27,480 92 | 2,12,1.51,2.42,22,86,1.45,1.25,.5,1.63,3.6,1.05,2.65,450 93 | 2,12.69,1.53,2.26,20.7,80,1.38,1.46,.58,1.62,3.05,.96,2.06,495 94 | 2,12.29,2.83,2.22,18,88,2.45,2.25,.25,1.99,2.15,1.15,3.3,290 95 | 2,11.62,1.99,2.28,18,98,3.02,2.26,.17,1.35,3.25,1.16,2.96,345 96 | 2,12.47,1.52,2.2,19,162,2.5,2.27,.32,3.28,2.6,1.16,2.63,937 97 | 2,11.81,2.12,2.74,21.5,134,1.6,.99,.14,1.56,2.5,.95,2.26,625 98 | 2,12.29,1.41,1.98,16,85,2.55,2.5,.29,1.77,2.9,1.23,2.74,428 99 | 2,12.37,1.07,2.1,18.5,88,3.52,3.75,.24,1.95,4.5,1.04,2.77,660 100 | 2,12.29,3.17,2.21,18,88,2.85,2.99,.45,2.81,2.3,1.42,2.83,406 101 | 2,12.08,2.08,1.7,17.5,97,2.23,2.17,.26,1.4,3.3,1.27,2.96,710 102 | 2,12.6,1.34,1.9,18.5,88,1.45,1.36,.29,1.35,2.45,1.04,2.77,562 103 | 2,12.34,2.45,2.46,21,98,2.56,2.11,.34,1.31,2.8,.8,3.38,438 104 | 2,11.82,1.72,1.88,19.5,86,2.5,1.64,.37,1.42,2.06,.94,2.44,415 105 | 2,12.51,1.73,1.98,20.5,85,2.2,1.92,.32,1.48,2.94,1.04,3.57,672 106 | 2,12.42,2.55,2.27,22,90,1.68,1.84,.66,1.42,2.7,.86,3.3,315 107 | 2,12.25,1.73,2.12,19,80,1.65,2.03,.37,1.63,3.4,1,3.17,510 108 | 2,12.72,1.75,2.28,22.5,84,1.38,1.76,.48,1.63,3.3,.88,2.42,488 109 | 2,12.22,1.29,1.94,19,92,2.36,2.04,.39,2.08,2.7,.86,3.02,312 110 | 2,11.61,1.35,2.7,20,94,2.74,2.92,.29,2.49,2.65,.96,3.26,680 111 | 2,11.46,3.74,1.82,19.5,107,3.18,2.58,.24,3.58,2.9,.75,2.81,562 112 | 2,12.52,2.43,2.17,21,88,2.55,2.27,.26,1.22,2,.9,2.78,325 113 | 2,11.76,2.68,2.92,20,103,1.75,2.03,.6,1.05,3.8,1.23,2.5,607 114 | 2,11.41,.74,2.5,21,88,2.48,2.01,.42,1.44,3.08,1.1,2.31,434 115 | 2,12.08,1.39,2.5,22.5,84,2.56,2.29,.43,1.04,2.9,.93,3.19,385 116 | 2,11.03,1.51,2.2,21.5,85,2.46,2.17,.52,2.01,1.9,1.71,2.87,407 117 | 2,11.82,1.47,1.99,20.8,86,1.98,1.6,.3,1.53,1.95,.95,3.33,495 118 | 2,12.42,1.61,2.19,22.5,108,2,2.09,.34,1.61,2.06,1.06,2.96,345 119 | 2,12.77,3.43,1.98,16,80,1.63,1.25,.43,.83,3.4,.7,2.12,372 120 | 2,12,3.43,2,19,87,2,1.64,.37,1.87,1.28,.93,3.05,564 121 | 2,11.45,2.4,2.42,20,96,2.9,2.79,.32,1.83,3.25,.8,3.39,625 122 | 2,11.56,2.05,3.23,28.5,119,3.18,5.08,.47,1.87,6,.93,3.69,465 123 | 2,12.42,4.43,2.73,26.5,102,2.2,2.13,.43,1.71,2.08,.92,3.12,365 124 | 2,13.05,5.8,2.13,21.5,86,2.62,2.65,.3,2.01,2.6,.73,3.1,380 125 | 2,11.87,4.31,2.39,21,82,2.86,3.03,.21,2.91,2.8,.75,3.64,380 126 | 2,12.07,2.16,2.17,21,85,2.6,2.65,.37,1.35,2.76,.86,3.28,378 127 | 2,12.43,1.53,2.29,21.5,86,2.74,3.15,.39,1.77,3.94,.69,2.84,352 128 | 2,11.79,2.13,2.78,28.5,92,2.13,2.24,.58,1.76,3,.97,2.44,466 129 | 2,12.37,1.63,2.3,24.5,88,2.22,2.45,.4,1.9,2.12,.89,2.78,342 130 | 2,12.04,4.3,2.38,22,80,2.1,1.75,.42,1.35,2.6,.79,2.57,580 131 | 3,12.86,1.35,2.32,18,122,1.51,1.25,.21,.94,4.1,.76,1.29,630 132 | 3,12.88,2.99,2.4,20,104,1.3,1.22,.24,.83,5.4,.74,1.42,530 133 | 3,12.81,2.31,2.4,24,98,1.15,1.09,.27,.83,5.7,.66,1.36,560 134 | 3,12.7,3.55,2.36,21.5,106,1.7,1.2,.17,.84,5,.78,1.29,600 135 | 3,12.51,1.24,2.25,17.5,85,2,.58,.6,1.25,5.45,.75,1.51,650 136 | 3,12.6,2.46,2.2,18.5,94,1.62,.66,.63,.94,7.1,.73,1.58,695 137 | 3,12.25,4.72,2.54,21,89,1.38,.47,.53,.8,3.85,.75,1.27,720 138 | 3,12.53,5.51,2.64,25,96,1.79,.6,.63,1.1,5,.82,1.69,515 139 | 3,13.49,3.59,2.19,19.5,88,1.62,.48,.58,.88,5.7,.81,1.82,580 140 | 3,12.84,2.96,2.61,24,101,2.32,.6,.53,.81,4.92,.89,2.15,590 141 | 3,12.93,2.81,2.7,21,96,1.54,.5,.53,.75,4.6,.77,2.31,600 142 | 3,13.36,2.56,2.35,20,89,1.4,.5,.37,.64,5.6,.7,2.47,780 143 | 3,13.52,3.17,2.72,23.5,97,1.55,.52,.5,.55,4.35,.89,2.06,520 144 | 3,13.62,4.95,2.35,20,92,2,.8,.47,1.02,4.4,.91,2.05,550 145 | 3,12.25,3.88,2.2,18.5,112,1.38,.78,.29,1.14,8.21,.65,2,855 146 | 3,13.16,3.57,2.15,21,102,1.5,.55,.43,1.3,4,.6,1.68,830 147 | 3,13.88,5.04,2.23,20,80,.98,.34,.4,.68,4.9,.58,1.33,415 148 | 3,12.87,4.61,2.48,21.5,86,1.7,.65,.47,.86,7.65,.54,1.86,625 149 | 3,13.32,3.24,2.38,21.5,92,1.93,.76,.45,1.25,8.42,.55,1.62,650 150 | 3,13.08,3.9,2.36,21.5,113,1.41,1.39,.34,1.14,9.40,.57,1.33,550 151 | 3,13.5,3.12,2.62,24,123,1.4,1.57,.22,1.25,8.60,.59,1.3,500 152 | 3,12.79,2.67,2.48,22,112,1.48,1.36,.24,1.26,10.8,.48,1.47,480 153 | 3,13.11,1.9,2.75,25.5,116,2.2,1.28,.26,1.56,7.1,.61,1.33,425 154 | 3,13.23,3.3,2.28,18.5,98,1.8,.83,.61,1.87,10.52,.56,1.51,675 155 | 3,12.58,1.29,2.1,20,103,1.48,.58,.53,1.4,7.6,.58,1.55,640 156 | 3,13.17,5.19,2.32,22,93,1.74,.63,.61,1.55,7.9,.6,1.48,725 157 | 3,13.84,4.12,2.38,19.5,89,1.8,.83,.48,1.56,9.01,.57,1.64,480 158 | 3,12.45,3.03,2.64,27,97,1.9,.58,.63,1.14,7.5,.67,1.73,880 159 | 3,14.34,1.68,2.7,25,98,2.8,1.31,.53,2.7,13,.57,1.96,660 160 | 3,13.48,1.67,2.64,22.5,89,2.6,1.1,.52,2.29,11.75,.57,1.78,620 161 | 3,12.36,3.83,2.38,21,88,2.3,.92,.5,1.04,7.65,.56,1.58,520 162 | 3,13.69,3.26,2.54,20,107,1.83,.56,.5,.8,5.88,.96,1.82,680 163 | 3,12.85,3.27,2.58,22,106,1.65,.6,.6,.96,5.58,.87,2.11,570 164 | 3,12.96,3.45,2.35,18.5,106,1.39,.7,.4,.94,5.28,.68,1.75,675 165 | 3,13.78,2.76,2.3,22,90,1.35,.68,.41,1.03,9.58,.7,1.68,615 166 | 3,13.73,4.36,2.26,22.5,88,1.28,.47,.52,1.15,6.62,.78,1.75,520 167 | 3,13.45,3.7,2.6,23,111,1.7,.92,.43,1.46,10.68,.85,1.56,695 168 | 3,12.82,3.37,2.3,19.5,88,1.48,.66,.4,.97,10.26,.72,1.75,685 169 | 3,13.58,2.58,2.69,24.5,105,1.55,.84,.39,1.54,8.66,.74,1.8,750 170 | 3,13.4,4.6,2.86,25,112,1.98,.96,.27,1.11,8.5,.67,1.92,630 171 | 3,12.2,3.03,2.32,19,96,1.25,.49,.4,.73,5.5,.66,1.83,510 172 | 3,12.77,2.39,2.28,19.5,86,1.39,.51,.48,.64,9.899999,.57,1.63,470 173 | 3,14.16,2.51,2.48,20,91,1.68,.7,.44,1.24,9.7,.62,1.71,660 174 | 3,13.71,5.65,2.45,20.5,95,1.68,.61,.52,1.06,7.7,.64,1.74,740 175 | 3,13.4,3.91,2.48,23,102,1.8,.75,.43,1.41,7.3,.7,1.56,750 176 | 3,13.27,4.28,2.26,20,120,1.59,.69,.43,1.35,10.2,.59,1.56,835 177 | 3,13.17,2.59,2.37,20,120,1.65,.68,.53,1.46,9.3,.6,1.62,840 178 | 3,14.13,4.1,2.74,24.5,96,2.05,.76,.56,1.35,9.2,.61,1.6,560 179 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/Sources/SwiftPriorityQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftPriorityQueue.swift 3 | // SwiftPriorityQueue 4 | // 5 | // Copyright (c) 2015-2017 David Kopec 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | // This code was inspired by Section 2.4 of Algorithms by Sedgewick & Wayne, 4th Edition 26 | 27 | /// A PriorityQueue takes objects to be pushed of any type that implements Comparable. 28 | /// It will pop the objects in the order that they would be sorted. A pop() or a push() 29 | /// can be accomplished in O(lg n) time. It can be specified whether the objects should 30 | /// be popped in ascending or descending order (Max Priority Queue or Min Priority Queue) 31 | /// at the time of initialization. 32 | public struct PriorityQueue { 33 | 34 | fileprivate var heap = [T]() 35 | private let ordered: (T, T) -> Bool 36 | 37 | public init(ascending: Bool = false, startingValues: [T] = []) { 38 | self.init(order: ascending ? { $0 > $1 } : { $0 < $1 }, startingValues: startingValues) 39 | } 40 | 41 | /// Creates a new PriorityQueue with the given ordering. 42 | /// 43 | /// - parameter order: A function that specifies whether its first argument should 44 | /// come after the second argument in the PriorityQueue. 45 | /// - parameter startingValues: An array of elements to initialize the PriorityQueue with. 46 | public init(order: @escaping (T, T) -> Bool, startingValues: [T] = []) { 47 | ordered = order 48 | 49 | // Based on "Heap construction" from Sedgewick p 323 50 | heap = startingValues 51 | var i = heap.count/2 - 1 52 | while i >= 0 { 53 | sink(i) 54 | i -= 1 55 | } 56 | } 57 | 58 | /// How many elements the Priority Queue stores 59 | public var count: Int { return heap.count } 60 | 61 | /// true if and only if the Priority Queue is empty 62 | public var isEmpty: Bool { return heap.isEmpty } 63 | 64 | /// Add a new element onto the Priority Queue. O(lg n) 65 | /// 66 | /// - parameter element: The element to be inserted into the Priority Queue. 67 | public mutating func push(_ element: T) { 68 | heap.append(element) 69 | swim(heap.count - 1) 70 | } 71 | 72 | /// Remove and return the element with the highest priority (or lowest if ascending). O(lg n) 73 | /// 74 | /// - returns: The element with the highest priority in the Priority Queue, or nil if the PriorityQueue is empty. 75 | public mutating func pop() -> T? { 76 | 77 | if heap.isEmpty { return nil } 78 | if heap.count == 1 { return heap.removeFirst() } // added for Swift 2 compatibility 79 | // so as not to call swap() with two instances of the same location 80 | heap.swapAt(0, heap.count - 1) 81 | let temp = heap.removeLast() 82 | sink(0) 83 | 84 | return temp 85 | } 86 | 87 | 88 | /// Removes the first occurence of a particular item. Finds it by value comparison using ==. O(n) 89 | /// Silently exits if no occurrence found. 90 | /// 91 | /// - parameter item: The item to remove the first occurrence of. 92 | public mutating func remove(_ item: T) { 93 | if let index = heap.index(of: item) { 94 | heap.swapAt(index, heap.count - 1) 95 | heap.removeLast() 96 | swim(index) 97 | sink(index) 98 | } 99 | } 100 | 101 | /// Removes all occurences of a particular item. Finds it by value comparison using ==. O(n) 102 | /// Silently exits if no occurrence found. 103 | /// 104 | /// - parameter item: The item to remove. 105 | public mutating func removeAll(_ item: T) { 106 | var lastCount = heap.count 107 | remove(item) 108 | while (heap.count < lastCount) { 109 | lastCount = heap.count 110 | remove(item) 111 | } 112 | } 113 | 114 | /// Get a look at the current highest priority item, without removing it. O(1) 115 | /// 116 | /// - returns: The element with the highest priority in the PriorityQueue, or nil if the PriorityQueue is empty. 117 | public func peek() -> T? { 118 | return heap.first 119 | } 120 | 121 | /// Eliminate all of the elements from the Priority Queue. 122 | public mutating func clear() { 123 | heap.removeAll(keepingCapacity: false) 124 | } 125 | 126 | // Based on example from Sedgewick p 316 127 | private mutating func sink(_ index: Int) { 128 | var index = index 129 | while 2 * index + 1 < heap.count { 130 | 131 | var j = 2 * index + 1 132 | 133 | if j < (heap.count - 1) && ordered(heap[j], heap[j + 1]) { j += 1 } 134 | if !ordered(heap[index], heap[j]) { break } 135 | 136 | heap.swapAt(index, j) 137 | index = j 138 | } 139 | } 140 | 141 | // Based on example from Sedgewick p 316 142 | private mutating func swim(_ index: Int) { 143 | var index = index 144 | while index > 0 && ordered(heap[(index - 1) / 2], heap[index]) { 145 | heap.swapAt((index - 1) / 2, index) 146 | index = (index - 1) / 2 147 | } 148 | } 149 | } 150 | 151 | // MARK: - GeneratorType 152 | extension PriorityQueue: IteratorProtocol { 153 | 154 | public typealias Element = T 155 | mutating public func next() -> Element? { return pop() } 156 | } 157 | 158 | // MARK: - SequenceType 159 | extension PriorityQueue: Sequence { 160 | 161 | public typealias Iterator = PriorityQueue 162 | public func makeIterator() -> Iterator { return self } 163 | } 164 | 165 | // MARK: - CollectionType 166 | extension PriorityQueue: Collection { 167 | 168 | public typealias Index = Int 169 | 170 | public var startIndex: Int { return heap.startIndex } 171 | public var endIndex: Int { return heap.endIndex } 172 | 173 | public subscript(i: Int) -> T { return heap[i] } 174 | 175 | public func index(after i: PriorityQueue.Index) -> PriorityQueue.Index { 176 | return heap.index(after: i) 177 | } 178 | } 179 | 180 | // MARK: - CustomStringConvertible, CustomDebugStringConvertible 181 | extension PriorityQueue: CustomStringConvertible, CustomDebugStringConvertible { 182 | 183 | public var description: String { return heap.description } 184 | public var debugDescription: String { return heap.debugDescription } 185 | } 186 | 187 | -------------------------------------------------------------------------------- /Classic Computer Science Problems in Swift.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Classic Computer Science Problems in Swift 2 | ![Classic Computer Science Problems in Swift Cover](cover.jpg) 3 | ## By David Kopec 4 | 5 | This is the Xcode Playground to accompany the book *Classic Computer Science Problems in Swift* by David Kopec. The book is available for purchase [directly from the publisher, Manning](https://www.manning.com/books/classic-computer-science-problems-in-swift?a_aid=oaksnow&a_bid=8de75028), and from other book vendors. 6 | 7 | ## Versioning 8 | 9 | The Playground is compatible with Swift 4 (Xcode 9). 10 | 11 | ## Links 12 | 13 | ### Get the Book 14 | 15 | - [Manning](https://www.manning.com/books/classic-computer-science-problems-in-swift?a_aid=oaksnow&a_bid=8de75028) 16 | - [Amazon](http://amzn.to/2xG6nlF) 17 | 18 | ### Classic Computer Science Problems Series 19 | 20 | - [Classic Computer Science Problems informational site](https://classicproblems.com/) 21 | - [Classic Computer Science Problems in Python](https://github.com/davecom/ClassicComputerScienceProblemsInPython) 22 | - [Classic Computer Science Problems in Java](https://github.com/davecom/ClassicComputerScienceProblemsInJava) 23 | 24 | ### Open Source Projects Mentioned in the Book 25 | 26 | - [SwiftPriorityQueue](https://github.com/davecom/SwiftPriorityQueue) 27 | - [SwiftGraph](https://github.com/davecom/SwiftGraph) 28 | - [SwiftCSP](https://github.com/davecom/SwiftCSP) 29 | - [SwiftSimpleNeuralNetwork](https://github.com/davecom/SwiftSimpleNeuralNetwork) 30 | 31 | ### Free Content Based on the Book 32 | 33 | - [Article: Solving Mazes with Swift](http://freecontent.manning.com/solving-mazes-with-swift/) 34 | - [Article: Tic-Tac-Toe AI in Swift](http://freecontent.manning.com/classic-computer-science-problems-in-swift-tic-tac-toe/) 35 | - [Slides: A* Search in Swift: Navigating a Maze](http://freecontent.manning.com/slideshare-a-search-in-swift-navigating-a-maze/) 36 | 37 | ### Other Books and Languages 38 | Official Books from the Series by @davecom 39 | - [Classic Computer Science Problems in Java](https://github.com/davecom/ClassicComputerScienceProblemsInJava) 40 | - [Classic Computer Science Problems in Python](https://github.com/davecom/ClassicComputerScienceProblemsInPython) 41 | 42 | My Latest Book 43 | - [Computer Science from Scratch: Building Interpreters, Art, Emulators and ML in Python](https://github.com/davecom/ComputerScienceFromScratch) 44 | 45 | Ports 46 | - [C++ implementation by @aray-andres](https://github.com/araya-andres/classic_computer_sci) 47 | - [Go implementation by @arlima](https://github.com/arlima/problemas_classicos_CC) 48 | - [PHP implementation by @SaschaKersken (German translator of CCSPiP)](https://github.com/SaschaKersken/ClassicComputerScienceProblemsInPhp) 49 | - [JavaScript implementation by @SaschaKersken (German translator of CCSPiP)](https://github.com/SaschaKersken/ClassicComputerScienceProblemsInJavaScript) 50 | - [Ruby implementation by @tj84](https://github.com/tj84/cs_problems) 51 | - [Rust implementation by @marpetercontribs](https://github.com/marpetercontribs/classic-computer-science-problems-in-rust) 52 | 53 | ## License 54 | 55 | The source code in this repository is licensed under the Apache License. See `LICENSE`. 56 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davecom/ClassicComputerScienceProblemsInSwift/46e23e08f39c4e48f225e1975b41f346a735eb16/cover.jpg --------------------------------------------------------------------------------