Ok, I got this to be 4x faster (almost exactly) on my machine by using an UnsafeMutableBufferPointer. This may be considered cheating, but it’s what I’d do in real life to eliminate retain/release/safe memory access.
Not that ¼ of the remaining time is from the random number generator!
Here’s my code.
import Foundation
typealias NodeIndex = Int
// MARK: -Node
fileprivate struct Node {
// MARK: properties
var x: Int
var y: Int = Int.random(in: 0...Int.max)
var left: NodeIndex?
var right: NodeIndex?
}
// MARK: -Tree
fileprivate class Tree {
// MARK: methods
func contains(_ value: Int) -> Bool {
let splitted = split(root: mRoot, value: value)
mRoot = merge(lower: splitted.lower, equal: splitted.equal, greater: splitted.greater)
return splitted.equal != nil
}
func insert(_ value: Int) {
var splitted = split(root: mRoot, value: value)
if splitted.equal == nil {
splitted.equal = nextNodeIndex
nodeHolder[nextNodeIndex] = Node(x: value)
nextNodeIndex += 1
}
mRoot = merge(lower: splitted.lower, equal: splitted.equal, greater: splitted.greater)
}
func erase(_ value: Int) {
let splited = split(root: mRoot, value: value)
mRoot = merge(lower: splited.lower, greater: splited.greater)
}
func merge(lower: NodeIndex?, equal: NodeIndex?, greater: NodeIndex?) -> NodeIndex? {
return merge(lower: merge(lower: lower, greater: equal), greater: greater)
}
func merge(lower: NodeIndex?, greater: NodeIndex?) -> NodeIndex? {
if let lower = lower, let greater = greater {
if nodeHolder[lower]!.y < nodeHolder[greater]!.y {
nodeHolder[lower]!.right = merge(lower: nodeHolder[lower]!.right, greater: greater)
return lower
} else {
nodeHolder[greater]!.left = merge(lower: lower, greater: nodeHolder[greater]!.left)
return greater
}
} else if lower == nil {
return greater
} else {
return lower
}
}
func split(root: NodeIndex?, value: Int) -> (lower: NodeIndex?, equal: NodeIndex?, greater: NodeIndex?) {
let (lower, equalGreater) = splitBinary(root: root, value: value)
let (equal, greater) = splitBinary(root: equalGreater, value: value + 1)
return (lower: lower, equal: equal, greater: greater)
}
func splitBinary(root: NodeIndex?, value: Int) -> (NodeIndex?, NodeIndex?) {
guard let root = root else { return (nil, nil) }
if nodeHolder[root]!.x < value {
let splitPair = splitBinary(root: nodeHolder[root]!.right, value: value)
nodeHolder[root]!.right = splitPair.0
return (root, splitPair.1)
} else {
let splitPair = splitBinary(root: nodeHolder[root]!.left, value: value)
nodeHolder[root]!.left = splitPair.1
return (splitPair.0, root)
}
}
// MARK: private properties
private var mRoot: NodeIndex?
private var nodeHolder = UnsafeMutableBufferPointer<Node?>.allocate(capacity: 1_000_000)
private var nextNodeIndex: NodeIndex = 0
}
// MARK: -The Test
func runNaive() -> Int
{
let tree = Tree()
var current = 5
var result = 0
for i in 1..<1_000_000 {
current = (current * 57 + 43) % 10007
switch i % 3 {
case 0: tree.insert(current)
case 1: tree.erase(current)
case 2: if tree.contains(current) { result += 1 }
default: break
}
}
return result
}
let startTime = Date().timeIntervalSince1970
print("Naive result \(runNaive())")
print("total time: \(Date().timeIntervalSince1970 - startTime)s")
