June 2018 Update
This commit is contained in:
parent
ba8067c3b7
commit
22f33d4004
5278 changed files with 84726 additions and 14379 deletions
|
|
@ -1,14 +1,12 @@
|
|||
import std.stdio, std.algorithm, std.math, std.random, std.typecons;
|
||||
import std.stdio, std.algorithm, std.math, std.random;
|
||||
|
||||
enum maxDim = 3;
|
||||
|
||||
struct KdNode {
|
||||
double[maxDim] x;
|
||||
struct KdNode(size_t dim) {
|
||||
double[dim] x;
|
||||
KdNode* left, right;
|
||||
}
|
||||
|
||||
// See QuickSelect method.
|
||||
KdNode* findMedian(size_t idx)(KdNode[] nodes) pure nothrow @nogc {
|
||||
KdNode!dim* findMedian(size_t idx, size_t dim)(KdNode!dim[] nodes) pure nothrow @nogc {
|
||||
auto start = nodes.ptr;
|
||||
auto end = &nodes[$ - 1] + 1;
|
||||
|
||||
|
|
@ -17,7 +15,7 @@ KdNode* findMedian(size_t idx)(KdNode[] nodes) pure nothrow @nogc {
|
|||
if (end == start + 1)
|
||||
return start;
|
||||
|
||||
KdNode* md = start + (end - start) / 2;
|
||||
auto md = start + (end - start) / 2;
|
||||
|
||||
while (true) {
|
||||
immutable double pivot = md.x[idx];
|
||||
|
|
@ -44,32 +42,32 @@ KdNode* findMedian(size_t idx)(KdNode[] nodes) pure nothrow @nogc {
|
|||
}
|
||||
}
|
||||
|
||||
KdNode* makeTree(size_t dim, size_t i)(KdNode[] nodes)
|
||||
KdNode!dim* makeTree(size_t dim, size_t i = 0)(KdNode!dim[] nodes)
|
||||
pure nothrow @nogc {
|
||||
if (!nodes.length)
|
||||
return null;
|
||||
|
||||
auto n = findMedian!i(nodes);
|
||||
auto n = nodes.findMedian!i;
|
||||
if (n != null) {
|
||||
enum i2 = (i + 1) % dim;
|
||||
immutable size_t nPos = n - nodes.ptr;
|
||||
n.left = makeTree!(dim, i2)(nodes[0 .. nPos]);
|
||||
n.left = makeTree!(dim, i2)(nodes[0 .. nPos]);
|
||||
n.right = makeTree!(dim, i2)(nodes[nPos + 1 .. $]);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void nearest(size_t dim)(in KdNode* root,
|
||||
in ref KdNode nd,
|
||||
void nearest(size_t dim)(in KdNode!dim* root,
|
||||
in ref KdNode!dim nd,
|
||||
in size_t i,
|
||||
ref const(KdNode)* best,
|
||||
ref const(KdNode!dim)* best,
|
||||
ref double bestDist,
|
||||
ref size_t nVisited) pure nothrow @safe @nogc {
|
||||
static double dist(in ref KdNode a, in ref KdNode b)
|
||||
static double dist(in ref KdNode!dim a, in ref KdNode!dim b)
|
||||
pure nothrow @nogc {
|
||||
typeof(KdNode.x[0]) result = 0;
|
||||
foreach (immutable i; staticIota!(0, dim))
|
||||
double result = 0;
|
||||
static foreach (i; 0 .. dim)
|
||||
result += (a.x[i] - b.x[i]) ^^ 2;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -101,50 +99,50 @@ void nearest(size_t dim)(in KdNode* root,
|
|||
nd, i2, best, bestDist, nVisited);
|
||||
}
|
||||
|
||||
void randPt(size_t dim=3)(ref KdNode v, ref Xorshift rng)
|
||||
void randPt(size_t dim)(ref KdNode!dim v, ref Xorshift rng)
|
||||
pure nothrow @safe @nogc {
|
||||
foreach (immutable i; staticIota!(0, dim))
|
||||
static foreach (i; 0 .. dim)
|
||||
v.x[i] = rng.uniform01;
|
||||
}
|
||||
|
||||
void smallTest() {
|
||||
KdNode[] wp = [{[2, 3]}, {[5, 4]}, {[9, 6]},
|
||||
/// smallTest
|
||||
unittest {
|
||||
KdNode!2[] wp = [{[2, 3]}, {[5, 4]}, {[9, 6]},
|
||||
{[4, 7]}, {[8, 1]}, {[7, 2]}];
|
||||
KdNode thisPt = {[9, 2]};
|
||||
KdNode!2 thisPt = {[9, 2]};
|
||||
|
||||
KdNode* root = makeTree!(2, 0)(wp);
|
||||
auto root = makeTree(wp);
|
||||
|
||||
const(KdNode)* found = null;
|
||||
const(KdNode!2)* found = null;
|
||||
double bestDist = 0;
|
||||
size_t nVisited = 0;
|
||||
nearest!2(root, thisPt, 0, found, bestDist, nVisited);
|
||||
root.nearest(thisPt, 0, found, bestDist, nVisited);
|
||||
|
||||
writefln("WP tree:\n Searching for %s\n" ~
|
||||
" Found %s, dist = %g\n Seen %d nodes.\n",
|
||||
thisPt.x[0..2], found.x[0..2], sqrt(bestDist), nVisited);
|
||||
thisPt.x, found.x, sqrt(bestDist), nVisited);
|
||||
}
|
||||
|
||||
void bigTest() {
|
||||
/// bigTest
|
||||
unittest {
|
||||
enum N = 1_000_000;
|
||||
enum testRuns = 100_000;
|
||||
|
||||
auto bigTree = new KdNode[N];
|
||||
auto bigTree = new KdNode!3[N];
|
||||
auto rng = 1.Xorshift;
|
||||
foreach (ref node; bigTree)
|
||||
randPt(node, rng);
|
||||
|
||||
KdNode* root = makeTree!(3, 0)(bigTree);
|
||||
KdNode thisPt;
|
||||
auto root = makeTree(bigTree);
|
||||
KdNode!3 thisPt;
|
||||
randPt(thisPt, rng);
|
||||
|
||||
const(KdNode)* found = null;
|
||||
const(KdNode!3)* found = null;
|
||||
double bestDist = 0;
|
||||
size_t nVisited = 0;
|
||||
nearest!3(root, thisPt, 0, found, bestDist, nVisited);
|
||||
root.nearest(thisPt, 0, found, bestDist, nVisited);
|
||||
|
||||
writefln("Big tree (%d nodes):\n Searching for %s\n"~
|
||||
" Found %s, dist = %g\n Seen %d nodes.",
|
||||
N, thisPt.x, found.x, sqrt(bestDist), nVisited);
|
||||
writefln("Big tree (%d nodes):\n Searching for %s\n" ~ " Found %s, dist = %g\n Seen %d nodes.", N, thisPt.x, found.x, sqrt(bestDist), nVisited);
|
||||
|
||||
size_t sum = 0;
|
||||
foreach (immutable _; 0 .. testRuns) {
|
||||
|
|
@ -154,12 +152,7 @@ void bigTest() {
|
|||
nearest!3(root, thisPt, 0, found, bestDist, nVisited);
|
||||
sum += nVisited;
|
||||
}
|
||||
writefln("\nBig tree:\n Visited %d nodes for %d random "~
|
||||
writefln("\nBig tree:\n Visited %d nodes for %d random " ~
|
||||
"searches (%.2f per lookup).",
|
||||
sum, testRuns, sum / double(testRuns));
|
||||
}
|
||||
|
||||
void main() {
|
||||
smallTest;
|
||||
bigTest;
|
||||
}
|
||||
|
|
|
|||
126
Task/K-d-tree/Kotlin/k-d-tree.kotlin
Normal file
126
Task/K-d-tree/Kotlin/k-d-tree.kotlin
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// version 1.1.51
|
||||
|
||||
import java.util.Random
|
||||
|
||||
typealias Point = DoubleArray
|
||||
|
||||
fun Point.sqd(p: Point) = this.zip(p) { a, b -> (a - b) * (a - b) }.sum()
|
||||
|
||||
class HyperRect (val min: Point, val max: Point) {
|
||||
fun copy() = HyperRect(min.copyOf(), max.copyOf())
|
||||
}
|
||||
|
||||
data class NearestNeighbor(val nearest: Point?, val distSqd: Double, val nodesVisited: Int)
|
||||
|
||||
class KdNode(
|
||||
val domElt: Point,
|
||||
val split: Int,
|
||||
var left: KdNode?,
|
||||
var right: KdNode?
|
||||
)
|
||||
|
||||
class KdTree {
|
||||
val n: KdNode?
|
||||
val bounds: HyperRect
|
||||
|
||||
constructor(pts: MutableList<Point>, bounds: HyperRect) {
|
||||
fun nk2(exset: MutableList<Point>, split: Int): KdNode? {
|
||||
if (exset.size == 0) return null
|
||||
val exset2 = exset.sortedBy { it[split] }
|
||||
for (i in 0 until exset.size) exset[i] = exset2[i]
|
||||
var m = exset.size / 2
|
||||
val d = exset[m]
|
||||
while (m + 1 < exset.size && exset[m + 1][split] == d[split]) m++
|
||||
var s2 = split + 1
|
||||
if (s2 == d.size) s2 = 0
|
||||
return KdNode(
|
||||
d,
|
||||
split,
|
||||
nk2(exset.subList(0, m), s2),
|
||||
nk2(exset.subList(m + 1, exset.size), s2)
|
||||
)
|
||||
}
|
||||
this.n = nk2(pts, 0)
|
||||
this.bounds = bounds
|
||||
}
|
||||
|
||||
fun nearest(p: Point) = nn(n, p, bounds, Double.POSITIVE_INFINITY)
|
||||
|
||||
private fun nn(
|
||||
kd: KdNode?,
|
||||
target: Point,
|
||||
hr: HyperRect,
|
||||
maxDistSqd: Double
|
||||
): NearestNeighbor {
|
||||
if (kd == null) return NearestNeighbor(null, Double.POSITIVE_INFINITY, 0)
|
||||
var nodesVisited = 1
|
||||
val s = kd.split
|
||||
val pivot = kd.domElt
|
||||
val leftHr = hr.copy()
|
||||
val rightHr = hr.copy()
|
||||
leftHr.max[s] = pivot[s]
|
||||
rightHr.min[s] = pivot[s]
|
||||
val targetInLeft = target[s] <= pivot[s]
|
||||
val nearerKd = if (targetInLeft) kd.left else kd.right
|
||||
val nearerHr = if (targetInLeft) leftHr else rightHr
|
||||
val furtherKd = if (targetInLeft) kd.right else kd.left
|
||||
val furtherHr = if (targetInLeft) rightHr else leftHr
|
||||
var (nearest, distSqd, nv) = nn(nearerKd, target, nearerHr, maxDistSqd)
|
||||
nodesVisited += nv
|
||||
var maxDistSqd2 = if (distSqd < maxDistSqd) distSqd else maxDistSqd
|
||||
var d = pivot[s] - target[s]
|
||||
d *= d
|
||||
if (d > maxDistSqd2) return NearestNeighbor(nearest, distSqd, nodesVisited)
|
||||
d = pivot.sqd(target)
|
||||
if (d < distSqd) {
|
||||
nearest = pivot
|
||||
distSqd = d
|
||||
maxDistSqd2 = distSqd
|
||||
}
|
||||
val temp = nn(furtherKd, target, furtherHr, maxDistSqd2)
|
||||
nodesVisited += temp.nodesVisited
|
||||
if (temp.distSqd < distSqd) {
|
||||
nearest = temp.nearest
|
||||
distSqd = temp.distSqd
|
||||
}
|
||||
return NearestNeighbor(nearest, distSqd, nodesVisited)
|
||||
}
|
||||
}
|
||||
|
||||
val rand = Random()
|
||||
|
||||
fun randomPt(dim: Int) = Point(dim) { rand.nextDouble() }
|
||||
|
||||
fun randomPts(dim: Int, n: Int) = MutableList<Point>(n) { randomPt(dim) }
|
||||
|
||||
fun showNearest(heading: String, kd: KdTree, p: Point) {
|
||||
println("$heading:")
|
||||
println("Point : ${p.asList()}")
|
||||
val (nn, ssq, nv) = kd.nearest(p)
|
||||
println("Nearest neighbor : ${nn?.asList()}")
|
||||
println("Distance : ${Math.sqrt(ssq)}")
|
||||
println("Nodes visited : $nv")
|
||||
println()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val points = mutableListOf(
|
||||
doubleArrayOf(2.0, 3.0),
|
||||
doubleArrayOf(5.0, 4.0),
|
||||
doubleArrayOf(9.0, 6.0),
|
||||
doubleArrayOf(4.0, 7.0),
|
||||
doubleArrayOf(8.0, 1.0),
|
||||
doubleArrayOf(7.0, 2.0)
|
||||
)
|
||||
var hr = HyperRect(doubleArrayOf(0.0, 0.0), doubleArrayOf(10.0, 10.0))
|
||||
var kd = KdTree(points, hr)
|
||||
showNearest("WP example data", kd, doubleArrayOf(9.0, 2.0))
|
||||
|
||||
hr = HyperRect(doubleArrayOf(0.0, 0.0, 0.0), doubleArrayOf(1.0, 1.0, 1.0))
|
||||
kd = KdTree(randomPts(3, 1000), hr)
|
||||
showNearest("1000 random 3D points", kd, randomPt(3))
|
||||
|
||||
hr = hr.copy()
|
||||
kd = KdTree(randomPts(3, 400_000), hr)
|
||||
showNearest("400,000 random 3D points", kd, randomPt(3))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue