Update all new Tasks
This commit is contained in:
parent
00a190b0a6
commit
91df62d461
5697 changed files with 93386 additions and 804 deletions
183
Task/K-d-tree/Go/k-d-tree.go
Normal file
183
Task/K-d-tree/Go/k-d-tree.go
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// Implmentation following pseudocode from "An intoductory tutorial on kd-trees"
|
||||
// by Andrew W. Moore, Carnegie Mellon University, PDF accessed from
|
||||
// http://www.autonlab.org/autonweb/14665
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// point is a k-dimensional point.
|
||||
type point []float64
|
||||
|
||||
// sqd returns the square of the euclidean distance.
|
||||
func (p point) sqd(q point) float64 {
|
||||
var sum float64
|
||||
for dim, pCoord := range p {
|
||||
d := pCoord - q[dim]
|
||||
sum += d * d
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// kdNode following field names in the paper.
|
||||
// rangeElt would be whatever data is associated with the point. we don't
|
||||
// bother with it for this example.
|
||||
type kdNode struct {
|
||||
domElt point
|
||||
split int
|
||||
left, right *kdNode
|
||||
}
|
||||
|
||||
type kdTree struct {
|
||||
n *kdNode
|
||||
bounds hyperRect
|
||||
}
|
||||
|
||||
type hyperRect struct {
|
||||
min, max point
|
||||
}
|
||||
|
||||
// Go slices are reference objects. The data must be copied if you want
|
||||
// to modify one without modifying the original.
|
||||
func (hr hyperRect) copy() hyperRect {
|
||||
return hyperRect{append(point{}, hr.min...), append(point{}, hr.max...)}
|
||||
}
|
||||
|
||||
// newKd constructs a kdTree from a list of points, also associating the
|
||||
// bounds of the tree. The bounds could be computed of course, but in this
|
||||
// example we know them already. The algorithm is table 6.3 in the paper.
|
||||
func newKd(pts []point, bounds hyperRect) kdTree {
|
||||
var nk2 func([]point, int) *kdNode
|
||||
nk2 = func(exset []point, split int) *kdNode {
|
||||
if len(exset) == 0 {
|
||||
return nil
|
||||
}
|
||||
// pivot choosing procedure. we find median, then find largest
|
||||
// index of points with median value. this satisfies the
|
||||
// inequalities of steps 6 and 7 in the algorithm.
|
||||
sort.Sort(part{exset, split})
|
||||
m := len(exset) / 2
|
||||
d := exset[m]
|
||||
for m+1 < len(exset) && exset[m+1][split] == d[split] {
|
||||
m++
|
||||
}
|
||||
// next split
|
||||
s2 := split + 1
|
||||
if s2 == len(d) {
|
||||
s2 = 0
|
||||
}
|
||||
return &kdNode{d, split, nk2(exset[:m], s2), nk2(exset[m+1:], s2)}
|
||||
}
|
||||
return kdTree{nk2(pts, 0), bounds}
|
||||
}
|
||||
|
||||
// a container type used for sorting. it holds the points to sort and
|
||||
// the dimension to use for the sort key.
|
||||
type part struct {
|
||||
pts []point
|
||||
dPart int
|
||||
}
|
||||
|
||||
// satisfy sort.Interface
|
||||
func (p part) Len() int { return len(p.pts) }
|
||||
func (p part) Less(i, j int) bool {
|
||||
return p.pts[i][p.dPart] < p.pts[j][p.dPart]
|
||||
}
|
||||
func (p part) Swap(i, j int) { p.pts[i], p.pts[j] = p.pts[j], p.pts[i] }
|
||||
|
||||
// nearest. find nearest neighbor. return values are:
|
||||
// nearest neighbor--the point within the tree that is nearest p.
|
||||
// square of the distance to that point.
|
||||
// a count of the nodes visited in the search.
|
||||
func (t kdTree) nearest(p point) (best point, bestSqd float64, nv int) {
|
||||
return nn(t.n, p, t.bounds, math.Inf(1))
|
||||
}
|
||||
|
||||
// algorithm is table 6.4 from the paper, with the addition of counting
|
||||
// the number nodes visited.
|
||||
func nn(kd *kdNode, target point, hr hyperRect,
|
||||
maxDistSqd float64) (nearest point, distSqd float64, nodesVisited int) {
|
||||
if kd == nil {
|
||||
return nil, math.Inf(1), 0
|
||||
}
|
||||
nodesVisited++
|
||||
s := kd.split
|
||||
pivot := kd.domElt
|
||||
leftHr := hr.copy()
|
||||
rightHr := hr.copy()
|
||||
leftHr.max[s] = pivot[s]
|
||||
rightHr.min[s] = pivot[s]
|
||||
targetInLeft := target[s] <= pivot[s]
|
||||
var nearerKd, furtherKd *kdNode
|
||||
var nearerHr, furtherHr hyperRect
|
||||
if targetInLeft {
|
||||
nearerKd, nearerHr = kd.left, leftHr
|
||||
furtherKd, furtherHr = kd.right, rightHr
|
||||
} else {
|
||||
nearerKd, nearerHr = kd.right, rightHr
|
||||
furtherKd, furtherHr = kd.left, leftHr
|
||||
}
|
||||
var nv int
|
||||
nearest, distSqd, nv = nn(nearerKd, target, nearerHr, maxDistSqd)
|
||||
nodesVisited += nv
|
||||
if distSqd < maxDistSqd {
|
||||
maxDistSqd = distSqd
|
||||
}
|
||||
d := pivot[s] - target[s]
|
||||
d *= d
|
||||
if d > maxDistSqd {
|
||||
return
|
||||
}
|
||||
if d = pivot.sqd(target); d < distSqd {
|
||||
nearest = pivot
|
||||
distSqd = d
|
||||
maxDistSqd = distSqd
|
||||
}
|
||||
tempNearest, tempSqd, nv := nn(furtherKd, target, furtherHr, maxDistSqd)
|
||||
nodesVisited += nv
|
||||
if tempSqd < distSqd {
|
||||
nearest = tempNearest
|
||||
distSqd = tempSqd
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
kd := newKd([]point{{2, 3}, {5, 4}, {9, 6}, {4, 7}, {8, 1}, {7, 2}},
|
||||
hyperRect{point{0, 0}, point{10, 10}})
|
||||
showNearest("WP example data", kd, point{9, 2})
|
||||
kd = newKd(randomPts(3, 1000), hyperRect{point{0, 0, 0}, point{1, 1, 1}})
|
||||
showNearest("1000 random 3d points", kd, randomPt(3))
|
||||
}
|
||||
|
||||
func randomPt(dim int) point {
|
||||
p := make(point, dim)
|
||||
for d := range p {
|
||||
p[d] = rand.Float64()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func randomPts(dim, n int) []point {
|
||||
p := make([]point, n)
|
||||
for i := range p {
|
||||
p[i] = randomPt(dim)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func showNearest(heading string, kd kdTree, p point) {
|
||||
fmt.Println()
|
||||
fmt.Println(heading)
|
||||
fmt.Println("point: ", p)
|
||||
nn, ssq, nv := kd.nearest(p)
|
||||
fmt.Println("nearest neighbor:", nn)
|
||||
fmt.Println("distance: ", math.Sqrt(ssq))
|
||||
fmt.Println("nodes visited: ", nv)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue