Skip to content

Commit 12f5a24

Browse files
committed
Update to follow Swift Style Guide
1 parent 62d1520 commit 12f5a24

File tree

2 files changed

+90
-45
lines changed

2 files changed

+90
-45
lines changed

K-Means/K-Means.playground/Contents.swift

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@ import Foundation
77

88
// Need a container to easily hold N Dimensional Vectors
99
class VectorND: CustomStringConvertible {
10-
private var length:Int = 0
11-
private var data:[Float] = [Float]()
10+
private var length = 0
11+
private var data = [Double]()
1212

13-
init(d:[Float]) {
14-
self.data = d
15-
self.length = d.count
13+
init(d:[Double]) {
14+
data = d
15+
length = d.count
1616
}
1717

18-
var description: String { return "VectorND (\(self.data)" }
19-
func getData() -> [Float] { return data }
18+
var description: String { return "VectorND (\(data)" }
19+
func getData() -> [Double] { return data }
2020
func getLength() -> Int { return length }
2121
}
2222

23-
// Ability to use std operators on VectorND object
23+
// MARK: VectorND Operators
2424
func +(left: VectorND, right: VectorND) -> VectorND {
25-
var results = [Float](count: left.getLength(), repeatedValue: 0.0)
25+
var results = [Double](count: left.getLength(), repeatedValue: 0.0)
2626
for idx in 0..<left.getLength() {
2727
results[idx] = left.getData()[idx] + right.getData()[idx]
2828
}
@@ -31,35 +31,39 @@ func +(left: VectorND, right: VectorND) -> VectorND {
3131
func +=(inout left: VectorND, right: VectorND) {
3232
left = left + right
3333
}
34-
func /(left:VectorND, right: Float) -> VectorND {
35-
var results = [Float](count: left.getLength(), repeatedValue: 0.0)
34+
func /(left:VectorND, right: Double) -> VectorND {
35+
var results = [Double](count: left.getLength(), repeatedValue: 0.0)
3636
for (idx, value) in left.getData().enumerate() {
3737
results[idx] = value / right
3838
}
3939
return VectorND(d: results)
4040
}
41-
func /=(inout left: VectorND, right: Float) {
41+
func /=(inout left: VectorND, right: Double) {
4242
left = left / right
4343
}
4444

45-
// TODO: Explain/Replace/Cleanup
46-
extension Array {
47-
var shuffle: [Element] {
48-
var elements = self
49-
for index in indices {
50-
let anotherIndex = Int(arc4random_uniform(UInt32(elements.count - index))) + index
51-
anotherIndex != index ? swap(&elements[index], &elements[anotherIndex]) : ()
52-
}
53-
return elements
45+
// MARK: Assist Functions
46+
// Pick a k random elements from samples
47+
func reservoirSample(samples:[VectorND], k:Int) -> [VectorND] {
48+
var result = [VectorND]()
49+
50+
// Fill the result array with first k elements
51+
for i in 0..<k {
52+
result.append(samples[i])
5453
}
55-
func choose(n: Int) -> [Element] {
56-
return Array(shuffle.prefix(n))
54+
// randomly replace elements from remaining ones
55+
for i in (k+1)..<samples.count {
56+
let j = Int(arc4random_uniform(UInt32(i+1)))
57+
if j < k {
58+
result[j] = samples[i]
59+
}
5760
}
61+
return result
5862
}
5963

6064
// Calculates the Euclidean distance between two VectorNDs
61-
func euclidean(v1:VectorND, v2:VectorND) -> Float {
62-
var result:Float = 0.0
65+
func euclidean(v1:VectorND, v2:VectorND) -> Double {
66+
var result = 0.0
6367
for idx in 0..<v1.getLength() {
6468
result += pow(v1.getData()[idx] - v2.getData()[idx], 2.0)
6569
}
@@ -68,9 +72,9 @@ func euclidean(v1:VectorND, v2:VectorND) -> Float {
6872

6973
// Get the INDEX of nearest Center to X
7074
func nearestCenter(x: VectorND, Centers: [VectorND]) -> Int {
71-
var nearestDist = FLT_MAX
72-
var minIndex:Int = 0;
73-
// Calculate the distance from VectorND X to all the centers
75+
var nearestDist = DBL_MAX
76+
var minIndex = 0;
77+
7478
for (idx, c) in Centers.enumerate() {
7579
let dist = euclidean(x, v2: c)
7680
if dist < nearestDist {
@@ -81,50 +85,56 @@ func nearestCenter(x: VectorND, Centers: [VectorND]) -> Int {
8185
return minIndex
8286
}
8387

84-
func kNN(numCenters: Int, convergeDist: Float, points: [VectorND]) -> [VectorND] {
85-
var centerMoveDist:Float = 0.0
86-
let zeros = [Float](count: points[0].getLength(), repeatedValue: 0.0)
88+
// MARK: Main Function
89+
func kMeans(numCenters: Int, convergeDist: Double, points: [VectorND]) -> [VectorND] {
90+
var centerMoveDist = 0.0
91+
let zeros = [Double](count: points[0].getLength(), repeatedValue: 0.0)
8792

8893
// 1. Choose k Random VectorNDs as the initial centers
89-
var kCenters:[VectorND] = points.choose(numCenters)
94+
var kCenters = reservoirSample(points, k: numCenters)
9095

9196
// do following steps until convergence
9297
repeat {
93-
var cnts = [Float](count: numCenters, repeatedValue: 0.0)
94-
var nCenters = [VectorND](count:numCenters, repeatedValue: VectorND(d:zeros))
98+
var cnts = [Double](count: numCenters, repeatedValue: 0.0)
99+
var newCenters = [VectorND](count:numCenters, repeatedValue: VectorND(d:zeros))
95100
// 2. Assign VectorNDs to centers
96101
// a. Determine which center each VectorND is closest to
97102
// b. Record how many VectorNDs are assigned to each center
98103
for p in points {
99104
let c = nearestCenter(p, Centers: kCenters)
100105
cnts[c]++
101-
nCenters[c] += p
106+
newCenters[c] += p
102107
}
103108
// 3. Calculate a new centers
104109
for idx in 0..<numCenters {
105-
nCenters[idx] /= cnts[idx]
110+
newCenters[idx] /= cnts[idx]
106111
}
107112
// 4. Determine how far centers moved
108113
centerMoveDist = 0.0
109114
for idx in 0..<numCenters {
110-
centerMoveDist += euclidean(kCenters[idx], v2: nCenters[idx])
115+
centerMoveDist += euclidean(kCenters[idx], v2: newCenters[idx])
111116
}
112117
// 5. Update centers to the newly calculated ones
113-
kCenters = nCenters
118+
kCenters = newCenters
114119
print("Complete iteration coverge(\(centerMoveDist) <? \(convergeDist))")
115120
} while(centerMoveDist > convergeDist)
116121
return kCenters
117122
}
118123

124+
// MARK: Sample Data
119125
var points = [VectorND]()
120-
let lim = 10
121-
for _ in 0..<lim {
122-
let x = Float(arc4random_uniform(UInt32(lim)))
123-
let y = Float(arc4random_uniform(UInt32(lim)))
124-
points.append(VectorND(d: [x, y]))
126+
let numPoints = 10
127+
let numDimmensions = 5
128+
for _ in 0..<numPoints {
129+
var data = [Double]()
130+
for x in 0..<numDimmensions {
131+
data.append(Double(arc4random_uniform(UInt32(numPoints*numDimmensions))))
132+
}
133+
points.append(VectorND(d: data))
125134
}
126135

127136
print("\nCenters")
128-
for c in kNN(3, convergeDist: 0.1, points: points) {
137+
for c in kMeans(3, convergeDist: 0.01, points: points) {
129138
print(c)
130-
}
139+
}
140+

K-Means/K-Means.playground/timeline.xctimeline

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,40 @@
22
<Timeline
33
version = "3.0">
44
<TimelineItems>
5+
<LoggerValueHistoryTimelineItem
6+
documentLocation = "#CharacterRangeLen=1&amp;CharacterRangeLoc=4157&amp;EndingColumnNumber=37&amp;EndingLineNumber=138&amp;StartingColumnNumber=1&amp;StartingLineNumber=138&amp;Timestamp=478154433.694733"
7+
selectedRepresentationIndex = "0"
8+
shouldTrackSuperviewWidth = "NO">
9+
</LoggerValueHistoryTimelineItem>
10+
<LoggerValueHistoryTimelineItem
11+
documentLocation = "file:///Users/johngill/git/swift-algorithm-club/K-Means/K-Means.playground#CharacterRangeLen=1&amp;CharacterRangeLoc=4157&amp;EndingLineNumber=138&amp;StartingLineNumber=138&amp;Timestamp=478154433.694858"
12+
selectedRepresentationIndex = "0"
13+
shouldTrackSuperviewWidth = "NO">
14+
</LoggerValueHistoryTimelineItem>
15+
<LoggerValueHistoryTimelineItem
16+
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=4157&amp;EndingColumnNumber=18&amp;EndingLineNumber=138&amp;StartingColumnNumber=5&amp;StartingLineNumber=138&amp;Timestamp=478154433.69495"
17+
selectedRepresentationIndex = "0"
18+
shouldTrackSuperviewWidth = "NO">
19+
</LoggerValueHistoryTimelineItem>
20+
<LoggerValueHistoryTimelineItem
21+
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=4157&amp;EndingColumnNumber=19&amp;EndingLineNumber=138&amp;StartingColumnNumber=5&amp;StartingLineNumber=138&amp;Timestamp=478154433.695038"
22+
selectedRepresentationIndex = "0"
23+
shouldTrackSuperviewWidth = "NO">
24+
</LoggerValueHistoryTimelineItem>
25+
<LoggerValueHistoryTimelineItem
26+
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=4157&amp;EndingColumnNumber=17&amp;EndingLineNumber=138&amp;StartingColumnNumber=9&amp;StartingLineNumber=138&amp;Timestamp=478154433.695117"
27+
selectedRepresentationIndex = "0"
28+
shouldTrackSuperviewWidth = "NO">
29+
</LoggerValueHistoryTimelineItem>
30+
<LoggerValueHistoryTimelineItem
31+
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=4157&amp;EndingColumnNumber=17&amp;EndingLineNumber=138&amp;StartingColumnNumber=9&amp;StartingLineNumber=138&amp;Timestamp=478154433.695195"
32+
selectedRepresentationIndex = "0"
33+
shouldTrackSuperviewWidth = "NO">
34+
</LoggerValueHistoryTimelineItem>
35+
<LoggerValueHistoryTimelineItem
36+
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=4157&amp;EndingColumnNumber=17&amp;EndingLineNumber=138&amp;StartingColumnNumber=9&amp;StartingLineNumber=138&amp;Timestamp=478154433.695273"
37+
selectedRepresentationIndex = "0"
38+
shouldTrackSuperviewWidth = "NO">
39+
</LoggerValueHistoryTimelineItem>
540
</TimelineItems>
641
</Timeline>

0 commit comments

Comments
 (0)