Update all new Tasks
This commit is contained in:
parent
00a190b0a6
commit
91df62d461
5697 changed files with 93386 additions and 804 deletions
209
Task/K-d-tree/D/k-d-tree-1.d
Normal file
209
Task/K-d-tree/D/k-d-tree-1.d
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// Implmentation following pseudocode from
|
||||
// "An introductory tutorial on kd-trees" by Andrew W. Moore,
|
||||
// Carnegie Mellon University, PDF accessed from:
|
||||
// http://www.autonlab.org/autonweb/14665
|
||||
|
||||
import std.typecons, std.math, std.algorithm, std.random, std.range,
|
||||
std.traits, core.memory;
|
||||
|
||||
/// k-dimensional point.
|
||||
struct Point(size_t k, F) if (isFloatingPoint!F) {
|
||||
F[k] data;
|
||||
alias data this; // Kills DMD std.algorithm.swap inlining.
|
||||
// Define opIndexAssign and opIndex for dmd.
|
||||
enum size_t length = k;
|
||||
|
||||
/// Square of the euclidean distance.
|
||||
double sqd(in ref Point!(k, F) q) const pure nothrow @nogc {
|
||||
double sum = 0;
|
||||
foreach (immutable dim, immutable pCoord; data)
|
||||
sum += (pCoord - q[dim]) ^^ 2;
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
struct KdNode(size_t k, F) {
|
||||
Point!(k, F) domElt;
|
||||
immutable int split;
|
||||
typeof(this)* left, right;
|
||||
}
|
||||
|
||||
struct Orthotope(size_t k, F) { /// k-dimensional rectangle.
|
||||
Point!(k, F) min, max;
|
||||
}
|
||||
|
||||
struct KdTree(size_t k, F) {
|
||||
KdNode!(k, F)* n;
|
||||
Orthotope!(k, F) bounds;
|
||||
|
||||
// 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.
|
||||
this(Point!(k, F)[] pts, in Orthotope!(k, F) bounds_) pure {
|
||||
static KdNode!(k, F)* nk2(size_t split)(Point!(k, F)[] exset)
|
||||
pure {
|
||||
if (exset.empty)
|
||||
return null;
|
||||
if (exset.length == 1)
|
||||
return new KdNode!(k, F)(exset[0], split, null, null);
|
||||
|
||||
// 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.
|
||||
auto m = exset.length / 2;
|
||||
topN!((p, q) => p[split] < q[split])(exset, m);
|
||||
immutable d = exset[m];
|
||||
while (m+1 < exset.length && exset[m+1][split] == d[split])
|
||||
m++;
|
||||
|
||||
enum nextSplit = (split + 1) % d.length;//cycle coordinates
|
||||
return new KdNode!(k, F)(d, split,
|
||||
nk2!nextSplit(exset[0 .. m]),
|
||||
nk2!nextSplit(exset[m + 1 .. $]));
|
||||
}
|
||||
|
||||
this.n = nk2!0(pts);
|
||||
this.bounds = bounds_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Find nearest neighbor. Return values are:
|
||||
nearest neighbor--the ooint within the tree that is nearest p.
|
||||
square of the distance to that point.
|
||||
a count of the nodes visited in the search.
|
||||
*/
|
||||
auto findNearest(size_t k, F)(KdTree!(k, F) t, in Point!(k, F) p)
|
||||
pure nothrow @nogc {
|
||||
// Algorithm is table 6.4 from the paper, with the addition of
|
||||
// counting the number nodes visited.
|
||||
static Tuple!(Point!(k, F), "nearest",
|
||||
F, "distSqd",
|
||||
int, "nodesVisited")
|
||||
nn(KdNode!(k, F)* kd, in Point!(k, F) target,
|
||||
Orthotope!(k, F) hr, F maxDistSqd) pure nothrow @nogc {
|
||||
if (kd == null)
|
||||
return typeof(return)(Point!(k, F)(), F.infinity, 0);
|
||||
|
||||
int nodesVisited = 1;
|
||||
immutable s = kd.split;
|
||||
auto pivot = kd.domElt;
|
||||
auto leftHr = hr;
|
||||
auto rightHr = hr;
|
||||
leftHr.max[s] = pivot[s];
|
||||
rightHr.min[s] = pivot[s];
|
||||
|
||||
KdNode!(k, F)* nearerKd, furtherKd;
|
||||
Orthotope!(k, F) nearerHr, furtherHr;
|
||||
if (target[s] <= pivot[s]) {
|
||||
//nearerKd, nearerHr = kd.left, leftHr;
|
||||
//furtherKd, furtherHr = kd.right, rightHr;
|
||||
nearerKd = kd.left;
|
||||
nearerHr = leftHr;
|
||||
furtherKd = kd.right;
|
||||
furtherHr = rightHr;
|
||||
} else {
|
||||
//nearerKd, nearerHr = kd.right, rightHr;
|
||||
//furtherKd, furtherHr = kd.left, leftHr;
|
||||
nearerKd = kd.right;
|
||||
nearerHr = rightHr;
|
||||
furtherKd = kd.left;
|
||||
furtherHr = leftHr;
|
||||
}
|
||||
|
||||
auto n1 = nn(nearerKd, target, nearerHr, maxDistSqd);
|
||||
auto nearest = n1.nearest;
|
||||
auto distSqd = n1.distSqd;
|
||||
nodesVisited += n1.nodesVisited;
|
||||
|
||||
if (distSqd < maxDistSqd)
|
||||
maxDistSqd = distSqd;
|
||||
auto d = (pivot[s] - target[s]) ^^ 2;
|
||||
if (d > maxDistSqd)
|
||||
return typeof(return)(nearest, distSqd, nodesVisited);
|
||||
d = pivot.sqd(target);
|
||||
if (d < distSqd) {
|
||||
nearest = pivot;
|
||||
distSqd = d;
|
||||
maxDistSqd = distSqd;
|
||||
}
|
||||
|
||||
immutable n2 = nn(furtherKd, target, furtherHr, maxDistSqd);
|
||||
nodesVisited += n2.nodesVisited;
|
||||
if (n2.distSqd < distSqd) {
|
||||
nearest = n2.nearest;
|
||||
distSqd = n2.distSqd;
|
||||
}
|
||||
|
||||
return typeof(return)(nearest, distSqd, nodesVisited);
|
||||
}
|
||||
|
||||
return nn(t.n, p, t.bounds, F.infinity);
|
||||
}
|
||||
|
||||
void showNearest(size_t k, F)(in string heading, KdTree!(k, F) kd,
|
||||
in Point!(k, F) p) {
|
||||
import std.stdio: writeln;
|
||||
writeln(heading, ":");
|
||||
writeln("Point: ", p);
|
||||
immutable n = kd.findNearest(p);
|
||||
writeln("Nearest neighbor: ", n.nearest);
|
||||
writeln("Distance: ", sqrt(n.distSqd));
|
||||
writeln("Nodes visited: ", n.nodesVisited, "\n");
|
||||
}
|
||||
|
||||
void main() {
|
||||
static Point!(k, F) randomPoint(size_t k, F)() {
|
||||
typeof(return) result;
|
||||
foreach (immutable i; 0 .. k)
|
||||
result[i] = uniform(F(0), F(1));
|
||||
return result;
|
||||
}
|
||||
|
||||
static Point!(k, F)[] randomPoints(size_t k, F)(in size_t n) {
|
||||
return n.iota.map!(_ => randomPoint!(k, F)).array;
|
||||
}
|
||||
|
||||
import std.stdio, std.conv, std.datetime, std.typetuple;
|
||||
rndGen.seed(1); // For repeatable outputs.
|
||||
|
||||
alias D2 = TypeTuple!(2, double);
|
||||
alias P = Point!D2;
|
||||
auto kd1 = KdTree!D2([P([2, 3]), P([5, 4]), P([9, 6]),
|
||||
P([4, 7]), P([8, 1]), P([7, 2])],
|
||||
Orthotope!D2(P([0, 0]), P([10, 10])));
|
||||
showNearest("Wikipedia example data", kd1, P([9, 2]));
|
||||
|
||||
enum int N = 400_000;
|
||||
alias F3 = TypeTuple!(3, float);
|
||||
alias Q = Point!F3;
|
||||
StopWatch sw;
|
||||
GC.disable;
|
||||
sw.start;
|
||||
auto kd2 = KdTree!F3(randomPoints!F3(N),
|
||||
Orthotope!F3(Q([0, 0, 0]), Q([1, 1, 1])));
|
||||
sw.stop;
|
||||
GC.enable;
|
||||
showNearest(text("k-d tree with ", N,
|
||||
" random 3D ", F3[1].stringof,
|
||||
" points (construction time: ",
|
||||
sw.peek.msecs, " ms)"), kd2, randomPoint!F3);
|
||||
|
||||
sw.reset;
|
||||
sw.start;
|
||||
enum int M = 10_000;
|
||||
size_t visited = 0;
|
||||
foreach (immutable _; 0 .. M) {
|
||||
immutable n = kd2.findNearest(randomPoint!F3);
|
||||
visited += n.nodesVisited;
|
||||
}
|
||||
sw.stop;
|
||||
|
||||
writefln("Visited an average of %0.2f nodes on %d searches " ~
|
||||
"in %d ms.", visited / double(M), M, sw.peek.msecs);
|
||||
}
|
||||
165
Task/K-d-tree/D/k-d-tree-2.d
Normal file
165
Task/K-d-tree/D/k-d-tree-2.d
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import std.stdio, std.algorithm, std.math, std.random, std.typecons;
|
||||
|
||||
enum maxDim = 3;
|
||||
|
||||
struct KdNode {
|
||||
double[maxDim] x;
|
||||
KdNode* left, right;
|
||||
}
|
||||
|
||||
// See QuickSelect method.
|
||||
KdNode* findMedian(size_t idx)(KdNode[] nodes) pure nothrow @nogc {
|
||||
auto start = nodes.ptr;
|
||||
auto end = &nodes[$ - 1] + 1;
|
||||
|
||||
if (end <= start)
|
||||
return null;
|
||||
if (end == start + 1)
|
||||
return start;
|
||||
|
||||
KdNode* md = start + (end - start) / 2;
|
||||
|
||||
while (true) {
|
||||
immutable double pivot = md.x[idx];
|
||||
|
||||
swap(md.x, (end - 1).x); // Swaps the whole arrays x.
|
||||
auto store = start;
|
||||
foreach (p; start .. end) {
|
||||
if (p.x[idx] < pivot) {
|
||||
if (p != store)
|
||||
swap(p.x, store.x);
|
||||
store++;
|
||||
}
|
||||
}
|
||||
swap(store.x, (end - 1).x);
|
||||
|
||||
// Median has duplicate values.
|
||||
if (store.x[idx] == md.x[idx])
|
||||
return md;
|
||||
|
||||
if (store > md)
|
||||
end = store;
|
||||
else
|
||||
start = store;
|
||||
}
|
||||
}
|
||||
|
||||
KdNode* makeTree(size_t dim, size_t i)(KdNode[] nodes)
|
||||
pure nothrow @nogc {
|
||||
if (!nodes.length)
|
||||
return null;
|
||||
|
||||
auto n = findMedian!i(nodes);
|
||||
if (n != null) {
|
||||
enum i2 = (i + 1) % dim;
|
||||
immutable size_t nPos = n - nodes.ptr;
|
||||
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,
|
||||
in size_t i,
|
||||
ref const(KdNode)* best,
|
||||
ref double bestDist,
|
||||
ref size_t nVisited) pure nothrow @safe @nogc {
|
||||
static double dist(in ref KdNode a, in ref KdNode b)
|
||||
pure nothrow @nogc {
|
||||
typeof(KdNode.x[0]) result = 0;
|
||||
foreach (immutable i; staticIota!(0, dim))
|
||||
result += (a.x[i] - b.x[i]) ^^ 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (root == null)
|
||||
return;
|
||||
|
||||
immutable double d = dist(*root, nd);
|
||||
immutable double dx = root.x[i] - nd.x[i];
|
||||
immutable double dx2 = dx ^^ 2;
|
||||
nVisited++;
|
||||
|
||||
if (!best || d < bestDist) {
|
||||
bestDist = d;
|
||||
best = root;
|
||||
}
|
||||
|
||||
// If chance of exact match is high.
|
||||
if (!bestDist)
|
||||
return;
|
||||
|
||||
immutable i2 = (i + 1 >= dim) ? 0 : i + 1;
|
||||
|
||||
nearest!dim(dx > 0 ? root.left : root.right,
|
||||
nd, i2, best, bestDist, nVisited);
|
||||
if (dx2 >= bestDist)
|
||||
return;
|
||||
nearest!dim(dx > 0 ? root.right : root.left,
|
||||
nd, i2, best, bestDist, nVisited);
|
||||
}
|
||||
|
||||
void randPt(size_t dim=3)(ref KdNode v, ref Xorshift rng)
|
||||
pure nothrow @safe @nogc {
|
||||
foreach (immutable i; staticIota!(0, dim))
|
||||
v.x[i] = rng.uniform01;
|
||||
}
|
||||
|
||||
void smallTest() {
|
||||
KdNode[] wp = [{[2, 3]}, {[5, 4]}, {[9, 6]},
|
||||
{[4, 7]}, {[8, 1]}, {[7, 2]}];
|
||||
KdNode thisPt = {[9, 2]};
|
||||
|
||||
KdNode* root = makeTree!(2, 0)(wp);
|
||||
|
||||
const(KdNode)* found = null;
|
||||
double bestDist = 0;
|
||||
size_t nVisited = 0;
|
||||
nearest!2(root, 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);
|
||||
}
|
||||
|
||||
void bigTest() {
|
||||
enum N = 1_000_000;
|
||||
enum testRuns = 100_000;
|
||||
|
||||
auto bigTree = new KdNode[N];
|
||||
auto rng = 1.Xorshift;
|
||||
foreach (ref node; bigTree)
|
||||
randPt(node, rng);
|
||||
|
||||
KdNode* root = makeTree!(3, 0)(bigTree);
|
||||
KdNode thisPt;
|
||||
randPt(thisPt, rng);
|
||||
|
||||
const(KdNode)* found = null;
|
||||
double bestDist = 0;
|
||||
size_t nVisited = 0;
|
||||
nearest!3(root, 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);
|
||||
|
||||
size_t sum = 0;
|
||||
foreach (immutable _; 0 .. testRuns) {
|
||||
found = null;
|
||||
nVisited = 0;
|
||||
randPt(thisPt, rng);
|
||||
nearest!3(root, thisPt, 0, found, bestDist, nVisited);
|
||||
sum += nVisited;
|
||||
}
|
||||
writefln("\nBig tree:\n Visited %d nodes for %d random "~
|
||||
"searches (%.2f per lookup).",
|
||||
sum, testRuns, sum / double(testRuns));
|
||||
}
|
||||
|
||||
void main() {
|
||||
smallTest;
|
||||
bigTest;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue