I should use the divide-and-conquer paradigm to design a recursive algorithm "CBCover", which determines a coverage (as seen in the image below) in runtime O(n^2) (n = 2^k).
The entry/ input of CBCover should be (k, a, b), where k defines the size of the chessboard, and (a, b) are the coordinates of the missing field.
Possible coverage:
4x4 chessboard missing (4, 1):
Does anyone have any idea what the pseudocode could look like?
The algorithm can be as follows:
Determine which of the four quadrants has the missing field. Position an L-piece in the centre of the grid such that it occupies one field in each of the three other quadrants. Now you have four quadrants with each a field that cannot be used any more. Solve each of those quadrants recursively, applying the same strategy. The recursion stops when the current grid has no available field, i.e. it is a 1x1 grid consisting of a field that is not available.
There are different possible data structures you could use to describe the tessalation. One way is to create a 2D grid, where each cell gets a value that uniquely identifies the shape it belongs to. So cells with the same value belong together.
The above algorithm could start with a grid that first assigns a unique value to each cell (0, 1, 2, ...etc), and then copies the value from one cell to another when they must belong to the same shape.
Here is an implementation in simple JavaScript. It is interactive, so you can change the input by clicking the buttons, and by hovering the mouse over the grid, you identify the "missing field":
// Main algorithm:
function tile(k, a, b) {
let length = 2**k;
// Create a grid where each cell has a unique value
let grid = [];
for (let y = 0; y < length; y++) {
let row = [];
for (let x = 0; x < length; x++) {
row.push(y*length + x); // unique value
}
grid.push(row);
}
a = a % length;
b = b % length;
function recur(length, a, b, top, left) {
if (length == 1) return;
let half = length / 2;
let midrow = top + half;
let midcol = left + half;
let quadrant = (a >= midrow) * 2 + (b >= midcol);
let val = -1;
for (let i = 0; i < 4; i++) {
let quadTop = i >= 2 ? midrow : top;
let quadLeft = i % 2 ? midcol : left;
let row, col;
if (quadrant == i) {
row = a;
col = b;
} else {
row = midrow - 1 + (i >> 1);
col = midcol - 1 + (i % 2);
// Add this cell to an L-shape
if (val == -1) val = grid[row][col];
else grid[row][col] = val; // join with neighboring cell
}
recur(half, row, col, quadTop, quadLeft);
}
}
recur(length, a, b, 0, 0);
return grid;
}
// Parameters of the problem
let k, a, b;
// I/O handling:
function solve(newK, newA, newB) {
if (newK <= 0) return; // grid must be at least 2x2
k = newK;
a = newA;
b = newB;
let grid = tile(k, a, b);
display(grid);
}
let table = document.querySelector("table");
function display(grid) {
table.innerHTML = "";
for (let y = 0; y < grid.length; y++) {
let tr = table.insertRow();
for (let x = 0; x < grid.length; x++) {
let val = grid[y][x];
cls = "";
if (y && val === grid[y-1][x]) cls += " up";
if (grid[y+1] && val === grid[y+1][x]) cls += " down";
if (val === grid[y][x-1]) cls += " left";
if (val === grid[y][x+1]) cls += " right";
if (cls === "") cls = "gap";
tr.insertCell().className = cls.trim();
}
}
}
// Allow user to select gap with a click on a cell:
table.addEventListener("mousemove", (e) => {
let td = e.target;
if (td.tagName !== "TD") return;
solve(k, td.parentNode.rowIndex, td.cellIndex);
});
// Allow user to change the size of the grid:
document.querySelector("#dbl").addEventListener("click", () => solve(k+1, a, b));
document.querySelector("#hlf").addEventListener("click", () => solve(k-1, a, b));
// Create, solve and display initial grid
solve(2, 0, 0);
table { border-collapse: collapse }
td { border: 1px solid; width: 10px; height: 10px }
.gap { background: silver }
.up { border-top-color: transparent }
.down { border-bottom-color: transparent }
.left { border-left-color: transparent }
.right { border-right-color: transparent }
<button id="dbl">Increase size</button><button id="hlf">Decrease size</button><br><br>
<table></table><br>
Related
Given a matrix of 0 and 1 (0 is free space, 1 is wall). Find the shortest path from one cell to another, passing only through 0 and also without touching 1.
enter image description here
How can I do this using Lee's Algorithm?
class Solution {
public:
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
// edge case: start or end not accessible
if (grid[0][0] || grid.back().back()) return -1;
// support variables
int res = 2, len = 1, maxX = grid[0].size() - 1, maxY = grid.size() - 1;
queue<pair<int, int>> q;
// edge case: single cell matrix
if (!maxX && !maxY) return 1 - (grid[0][0] << 1);
// adding the starting point
q.push({0, 0});
// marking start as visited
grid[0][0] = -1;
while (len) {
while (len--) {
// reading and popping the coordinates on the front of the queue
auto [cx, cy] = q.front();
q.pop();
for (int x = max(0, cx - 1), lmtX = min(cx + 1, maxX); x <= lmtX; x++) {
for (int y = max(0, cy - 1), lmtY = min(cy + 1, maxY); y <= lmtY; y++) {
// check if we reached the target
if (x == maxX && y == maxY) return res;
// marking it as visited and adding it to the q if it was still a valid cell
if (!grid[y][x]) {
grid[y][x] = -1;
q.push({x, y});
}
}
}
}
// preparing for the next loop
res++;
len = q.size();
}
return -1;
}
};
On a chessboard consisting of M rows and N columns (for example, 8x10), there are two knights, the user enters their coordinates himself (for example, (2, 4) is a white knight and (7, 9) is a black knight). Each knight is located in the it's cell, but it is possible that both knights are in the same cell.
The knights take turns making moves in accordance with the rules of the chess knight's movement (the white knight goes first). The goal of the game is to place both horses in the same cell as quickly as possible.
Input format
The first line of the file contains the values M and N (2≤M,N≤1000). The second and third lines contain the coordinates of the cells in which the white and black knight are located, respectively. The first coordinate is in the range from 1 to M, the second is in the range from 1 to N.
Output format
Print a single number — the number of moves required to complete the game. If the knights can never be placed in the same square, print -1.
Since I'm new to algorithms and data structures, I tried to solve this problem like this: run for loop on all 64 possible combinations of two moves of a white and black knight, make a move for each knight (checking if it goes beyond the scope), check if there is a match and, if there is, then output it. Then run the same cycle inside of the current. At the same time, the moves are counted and it is also output. However, I have encountered such a problem that I cannot automate the process of running this loop inside the loop, I cannot know the number of times that this loop needs to be run. I tried to create a function with recursion in which it was possible to call this loop if a match has not yet been found, but I failed.
I decided that it would not work that way to solve this problem, so I looked at the algorithms that are usually used in such tasks. I was thinking of somehow creating an adjacency list for two horses, where the vertices are all the calculated positions of the horse; use BFS, or the Dijkstra algorithm.
Solved.
Here is my swift code:
import Foundation
let mnstr = readLine()?.components(separatedBy: " ")
let m = Int(mnstr![0])!
let n = Int(mnstr![1])!
let wstr = readLine()?.components(separatedBy: " ")
let bstr = readLine()?.components(separatedBy: " ")
var w: [Int] = []
var b: [Int] = []
var count: Int = 0
let moves: [[Int]] = [[2, -1], [1, 2], [-2, -1], [1, -2], [2, 1], [-1, 2], [-2, 1], [-1, -2]]
w.append(Int(wstr![0])!)
w.append(Int(wstr![1])!)
b.append(Int(bstr![0])!)
b.append(Int(bstr![1])!)
var wp: Set = [w]
var bp: Set = [b]
func oneMove(lst: Set<[Int]>) -> Set<[Int]>{
let curr = lst
var out = lst
for i in curr {
for move in moves {
let item = [i[0] + move[0], i[1] + move[1]]
if item[0] < 1 || item[0] > m || item[1] < 1 || item[1] > n {
continue
}
out.insert(item)
}
}
return out
}
while bp.intersection(wp).isEmpty == true {
wp = oneMove(lst: wp)
count += 1
if wp.intersection(bp).isEmpty != true {
break
}
bp = oneMove(lst: bp)
count += 1
if wp.intersection(bp).isEmpty != true {
break
}
if wp.count == 1 || bp.count == 1 {
count = -1
break
}
}
print(count)
I know that an answer was already accepted, but for large distances between the pieces a BFS or Dijkstra's algorithm will use considerable time and resources.
There is however a pattern: when there is enough distance between the pieces (both in X and Y direction), an optimal path can be found within the bounding box of the two pieces and can be derived by a closed formula. And the more constrained or unsolvable situations can be identified also in constant time. The code for distinguishing the different patterns is quite "dull", but it will certainly run faster for when the paths are long: in constant time (if we assume arithmetic operations use constant time).
Here is some JavaScript code, which also includes the BFS algorithm so the outcome can be compared. It includes an interactive part, so that you can play with the board sizes and the positioning of the two pieces and check the results:
function knightDistance(rowCount, colCount, whiteX, whiteY, blackX, blackY) {
// Convert the state so to ensure that black is at the right & upper side of white, and below the diagonal
if (blackX < whiteX) return knightDistance(rowCount, colCount, blackX, blackY, whiteX, whiteY); // Swap pieces
if (blackY < whiteY) return knightDistance(rowCount, colCount, whiteX, rowCount - 1 - whiteY, blackX, rowCount - 1 - blackY); // Mirror against X axis
let diffX = blackX - whiteX;
let diffY = blackY - whiteY;
if (diffX < diffY) return knightDistance(colCount, rowCount, whiteY, whiteX, blackY, blackX); // mirror along diagonal
if (diffX == 2 && diffY == 2) return 4;
if (diffX <= 2 * diffY && diffX != 1) {
if ((diffX + diffY) % 2) return Math.floor((diffX + diffY + 1) / 6) * 2 + 1;
return Math.floor((diffX + diffY + 4) / 6) * 2;
}
if (rowCount == 1 || colCount == 2) return -1;
if (rowCount == 2 && diffX % 4 != 2 * diffY) return -1;
if (diffX + diffY > 3) {
if ((diffX + diffY) % 2) return Math.floor((diffX + 1) / 4) * 2 + 1;
return Math.floor((diffX + 3) / 4) * 2;
}
// Now rowCount > 2 and colCount > 2
// Other cases where lack of space plays a role
if (diffY == 1) {
// Now diffX == 1
if (rowCount == 3 && colCount == 3 && whiteX == whiteY) return -1;
if (whiteX == 0 && whiteY == 0 || blackX == colCount - 1 && blackY == rowCount - 1) return 4;
return 2;
}
// Now diffY == 0
if (diffX == 1) {
if (whiteY == 1 && rowCount == 3 && colCount == 3) return -1;
if (whiteY == 1 && rowCount == 3 && colCount == 4 && whiteX == 1) return 5;
return 3;
}
if (diffX == 2) {
if (whiteY == 1 && rowCount == 3) return 4;
return 2;
}
// Now diffY == 3
if (colCount == 4 && (whiteY == 0 || whiteY == rowCount - 1)) return 5;
return 3;
}
// The BFS algorithm for verification of the above function
function knightDistanceBfs(rowCount, colCount, whiteX, whiteY, blackX, blackY) {
let visited = new Set;
let frontier = [[whiteX, whiteY]];
visited.add(whiteX + whiteY * colCount);
let steps = 0;
while (frontier.length) {
let newFrontier = [];
for (let [whiteX, whiteY] of frontier) {
if (whiteX == blackX && whiteY == blackY) return steps;
for (let [dx, dy] of [[-2, -1], [2, -1], [2, 1], [-2, 1], [-1, -2], [1, -2], [1, 2], [-1, 2]]) {
let newX = whiteX + dx;
let newY = whiteY + dy;
if (newX < 0 || newY < 0 || newX >= colCount || newY >= rowCount) continue;
let key = newX + newY * colCount;
if (visited.has(key)) continue;
visited.add(key);
newFrontier.push([newX, newY]);
}
}
steps++;
frontier = newFrontier;
}
return -1;
}
// Quick test of all possibilities on boards with at most 5 rows and 5 columns:
for (let rowCount = 1; rowCount <= 5; rowCount++) {
for (let colCount = 1; colCount <= 5; colCount++) {
for (let whiteX = 0; whiteX < colCount; whiteX++) {
for (let whiteY = 0; whiteY < rowCount; whiteY++) {
for (let blackX = 0; blackX < colCount; blackX++) {
for (let blackY = 0; blackY < rowCount; blackY++) {
let answer = knightDistanceBfs(rowCount, colCount, whiteX, whiteY, blackX, blackY);
let answer2 = knightDistance(rowCount, colCount, whiteX, whiteY, blackX, blackY);
if (answer !== answer2) {
console.log({rowCount, colCount, whiteX, whiteY, blackX, blackY});
throw "Test case failed";
}
}
}
}
}
}
}
// I/O handling
let [rowInput, colInput] = document.querySelectorAll("input");
let table = document.querySelector("table");
let outputs = document.querySelectorAll("span");
let whiteX, whiteY, blackX, blackY;
rowInput.oninput = colInput.oninput = function () {
// Create table
table.innerHTML = "";
for (let i = +rowInput.value; i > 0; i--) {
let row = table.insertRow();
for (let j = +colInput.value; j > 0; j--) {
row.insertCell();
}
}
whiteX = -1;
blackX = -1;
};
table.onclick = function (e) {
if (e.target.tagName != "TD") return;
let x = e.target.cellIndex;
let y = e.target.parentNode.rowIndex;
if (x == whiteX && y == whiteY) {
e.target.textContent = "";
whiteX = -1;
whiteY = -1;
} else if (x == blackX && y == blackY) {
e.target.textContent = "";
blackX = -1;
blackY = -1;
} else if (whiteX == -1) {
e.target.textContent = "♘";
whiteX = x;
whiteY = y;
} else {
if (blackX != -1) { // Remove black piece first
table.rows[blackY].cells[blackX].textContent = "";
}
e.target.textContent = "♞";
blackX = x;
blackY = y;
}
if (blackX != -1 && whiteX != -1) {
outputs[0].textContent = knightDistanceBfs(+rowInput.value, +colInput.value, whiteX, whiteY, blackX, blackY);
outputs[1].textContent = knightDistance(+rowInput.value, +colInput.value, whiteX, whiteY, blackX, blackY);
} else {
outputs[0].textContent = outputs[1].textContent = "--";
}
}
rowInput.oninput();
table { border-collapse: collapse; cursor: pointer; margin: 2px }
td { border: 1px solid; width: 22px; height: 22px; padding: 0 }
input { width: 3em }
<div>Rows: <input id="rows" type="number" value="3"> Columns: <input id="cols" type="number" value="3"></div>
<table></table>
Number of moves: <span>--</span> (with BFS: <span>--</span>)
<div>Click on the board to place/remove pieces</div>
This would seem to be the basic logic you want, where ?_locs is a set of the locations a particular knight can be in (initialized to its initial location) and one_move yields a set of the locations that can be reached in 1 move from one of the locations in the argument:
while bk_locs intersect wh_locs is empty:
bk_locs = one_move(bk_locs)
wh_locs = one_move(wh_locs)
What this doesn't handle is counting moves (trivial) or identifying when to give up (harder).
Given a number x, insert elements 1 to x^2 in a matrix spirally.
e.g. For x = 3, matrix looks like [[1,2,3],[8,9,4],[7,6,5]].
For this I've written following snippet. However, I'm getting o/p as [[7,9,5],[7,9,5],[7,9,5]]
while(t<=b && l<=r){
System.out.print(t+" "+b+" "+l+" "+r+"\n");
if(dir==0){
for(int i = l;i<=r;i++){
arr.get(t).set(i,x);
x++;
}
t++;
}else if(dir==1){
for(int i = t;i<=b;i++){
arr.get(i).set(r,x);
x++;
}
r--;
}else if(dir==2){
for(int i = r;i>=l;i--){
arr.get(b).set(i,x);
x++;
}
b--;
}else if(dir==3){
for(int i = b;i>=t;i--){
arr.get(l).set(i,x);
x++;
}
l++;
}
dir = (dir+1)%4;
}
You can use the next code (which I developed for some implementation that handles huge martrix sizes). It will use width (columns) and height (rows) of any matrix size and produce the output you need
List<rec> BuildSpiralIndexList(long w, long h)
{
List<rec> result = new List<rec>();
long count = 0,dir = 1,phase = 0,pos = 0;
long length = 0,totallength = 0;
bool isVertical = false;
if ((w * h)<1) return null;
do
{
isVertical = (count % 2) != 0;
length = (isVertical ? h : w) - count / 2 - count % 2;
phase = (count / 4);
pos = (count % 4);
dir = pos > 1 ? -1 : 1;
for (int t = 0; t < length; t++)
// you can replace the next code with printing or any other action you need
result.Add(new rec()
{
X = ((pos == 2 || pos == 1) ? (w - 1 - phase - (pos == 2 ? 1 : 0)) : phase) + dir * (isVertical ? 0 : t),
Y = ((pos <= 1 ? phase + pos : (h - 1) - phase - pos / 3)) + dir * (isVertical ? t : 0),
Index = totallength + t
});
totallength += length;
count++;
} while (totallength < (w*h));
return result;
}
This solution walks from the top left to the top right, the top right to the bottom right, the bottom right to the bottom left and the bottom left up to the top left.
It is a tricky problem, hopefully my comments below assist in explaining.
Below is a codepen link to see it added to a table.
https://codepen.io/mitchell-boland/pen/rqdWPO
const n = 3; // Set this to a number
matrixSpiral(n);
function matrixSpiral(number){
// Will populate the outer array with n-times inner arrays
var outerArray = [];
for(var i = 0; i < number; i++){
outerArray.push([]);
}
var leftColumn = 0;
var rightColumn = number - 1;
var topRow = 0;
var bottomRow = number-1;
var counter = 1; // Used to track the number we are up to.
while(leftColumn <= rightColumn && topRow <=bottomRow){
// populate the top row
for(var i = leftColumn; i <= rightColumn; i++){
outerArray[leftColumn][i] = counter;
counter++;
}
// Top row is now populated
topRow ++;
// Populate the right column
for(var i = topRow ; i <= bottomRow; i++){
outerArray[i][rightColumn] = counter;
counter++;
}
// Right column now populated.
rightColumn--;
// Populate the bottom row
// We are going from the bottom right, to the bottom left
for(var i = rightColumn; i >= leftColumn; i--){
outerArray[bottomRow][i] = counter;
counter++;
}
// Bottom Row now populated
bottomRow--;
// Populate the left column
// We are going from bottom left, to top left
for(var i = bottomRow; i >= topRow ; i--){
outerArray[i][leftColumn] = counter;
counter++;
}
// Left column now populated.
leftColumn++;
// While loop will now repeat the above process, but a step in.
}
// Console log the results.
for(var i = 0; i < number; i++){
console.log(outerArray[i]);
}
}
I am looking for an algorithm that would get me data necessary for drawing labeled regions of a matrix in a 3d application.
The input looks like this:
For each region I need to find vertices of its outer boundary in CCW order.
I already can find the vertices of all horizontal or vertical edges by looking at the neighbours, but my implementation finds vertices from left to right, from top to bottom and not in the CCW order. Here is my code.
for (int i = 1; i < columns-1; i++)
for (int j = 1; j < rows - 1; j++) {
if (grid[i][j] > 0) { // not background
if ((grid[i + 1][j] != id) && (grid[i][j - 1] != id)) {
getCellTopLeftCoord(i, j, &x, &y);
polyPath[id]->Add(gcnew mPoint(x + width, y));
}
if ((grid[i - 1][j] != id) && (grid[i][j - 1] != id)) {
getCellTopLeftCoord(i, j, &x, &y);
polyPath[id]->Add(gcnew mPoint(x, y));
}
... // etc..
here are the boundaries I am interested in:
The following procedure should work if there aren't any unconnected surfaces with repeated labels:
Traverse the matrix from top to bottom and from left to right. If you encounter a non-null cell with a label that you haven't treated yet, create the path for that label.
The point you have found is guaranteed to be a northeast corner. Put that point into your path.
Now create a list of directions and start by going south. Because you are walking along the border anticlockwise, you should always have an occupied cell to the left and an unoccupied cell to the right. (Occupied here refers to a cell with the desired label.)
When you try to find the next direction, continue in the last direction and check the cells to your right and left. if both are unoccupied, turn left. If at least the right one is occupied, turn right. Otherwise, continues straight on.
When you change direction, append the current point to your path.
Update the coordinates acording to the current direction. Repeat until you reach your original coordinates.
This method will not give you the diagonal lines around the area labelled 4 in your sketch; it will follow the axis-aligned jagged outline.
Here's an example implementation in Javascript. The cell data is contained in the two-dimensional array m. cell looks up a cell, but accounts for out-of bounds look-ups. path creates the path for a single label. paths creates a list of paths; it calls path:
function cell(x, y) {
if (y < 0) return 0;
if (y >= m.length) return 0;
if (x < 0) return 0;
if (x >= m[y].length) return 0;
return m[y][x];
}
function path(x, y, c) {
var x0 = x;
var y0 = y;
var res = [{x: x, y: y}];
var dir = "s";
var l, r;
y++;
while (x != x0 || y != y0) {
var old = dir;
switch (dir) {
case "n": l = (cell(x - 1, y - 1) == c) ? 1 : 0;
r = (cell(x, y - 1) == c) ? 2 : 0;
dir = ["w", "n", "e", "e"][l + r];
break;
case "e": l = (cell(x, y - 1) == c) ? 1 : 0;
r = (cell(x, y) == c) ? 2 : 0;
dir = ["n", "e", "s", "s"][l + r];
break;
case "s": l = (cell(x, y) == c) ? 1 : 0;
r = (cell(x - 1, y) == c) ? 2 : 0;
dir = ["e", "s", "w", "w"][l + r];
break;
case "w": l = (cell(x - 1, y) == c) ? 1 : 0;
r = (cell(x - 1, y - 1) == c) ? 2 : 0;
dir = ["s", "w", "n", "n"][l + r];
break;
}
if (dir != old) res.push({x: x, y: y});
switch (dir) {
case "n": y--; break;
case "e": x++; break;
case "s": y++; break;
case "w": x--; break;
}
}
return res;
}
function paths() {
var res = {};
for (var y = 0; y < m.length; y++) {
for (var x = 0; x < m[y].length; x++) {
var c = m[y][x];
if (c && !(c in res)) {
res[c] = path(x, y, c);
}
}
}
return res;
}
I would like to develop a range searching algorithm that reports all points within a given distance of a query point.
The points are specified by d integer coordinates in a tiny range, say up to 6 bits per dimension (range 0..63), for a total bit count not exceeding 60 bits.
The distance metric is Manhattan or Euclidean (up to you), i.e. the sum of absolute or squared coordinate differences. In the special case of a single bit per dimension, it amounts to the Hamming distance.
There can be up to a million points.
Are you aware of a practical data structure that supports fast queries, say O(Log²(n)+k) or similar (with space O(n)) in such conditions ? A reasonable preprocessing time (subquadratic) is also required.
k-D trees are a first option, but they don't exploit the finiteness of the coordinates and are likely to perform poorly in high dimensions, I am afraid.
The case of a single bit per coordinate is especially interesting. Even partial solutions are welcome.
After some thought (and prodding by #YvesDaoust) using a VP Tree (Vantage Point Tree https://en.wikipedia.org/wiki/Vantage-point_tree) is probably the best solution.
VP Tree is a BSP where the left nodes are inside the distance and the right nodes are outside of the distance. This works for single bit per dimension and multiple bit per dimension (only the distance formula would change. The distance is a per tree node threshold/radius. Querying involves recursing through the tree getting current node value's distance to the query value and comparing that result with the query distance.
JSFiddle http://jsfiddle.net/fgq1rfLk/
var DIMS = 16;
var BITS = 1;
var MASK = (Math.pow(2, BITS) - 1)|0;
var SIZE = DIMS * BITS;
var list = [];
var tree = null;
//
// set bit count (population count)
function popCnt(x) {
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x = x + (x >> 8);
x = x + (x >> 16);
return x & 0x0000003F;
}
//
// manhattan distance
function dist(a, b) {
if(BITS == 1) {
return popCnt(a ^ b);
}
var result = 0;
for(var i=0; i<DIMS; i++) {
var shr = i * BITS;
result += Math.abs(((a >> shr) & MASK) - ((b >> shr) & MASK));
}
return result;
}
//
// Vantage point tree
// max size of tree leaf nodes
VP_LEAF_SIZE = 32;
// need to choose a reasonable maximum distance
VP_DISTANCE = (BITS === 1) ? SIZE : 32;
function VPTree(data) {
this.radius = null;
this.center = null;
this.values = null;
this.inside = null;
this.outside = null;
//
var n = data.length;
var r = data[0];
// leaf node?
if(n <= VP_LEAF_SIZE || n <= 1) {
this.values = [].concat(data);
return this;
}
this.center = r;
// process data for counts at all possible distances
var buckets = Array(VP_DISTANCE + 1);
for(var i=0; i<=VP_DISTANCE; i++) {
buckets[i] = 0;
}
// distance counts
for(var i=0; i<n; i++) {
var v = data[i];
var d = dist(r, v);
if(d < VP_DISTANCE) {
buckets[d]++;
} else {
buckets[VP_DISTANCE]++;
}
}
// distance offsets
var sum = 0;
for(var i=0; i<=VP_DISTANCE; i++) {
buckets[i] = (sum += buckets[i]);
}
// pivot index
var median = n >> 1;
var pivot = 1;
for(var i=1; i<=VP_DISTANCE; i++) {
if(buckets[i] > median) {
pivot = (i > 1 && median - buckets[i - 1] <= buckets[i] - median) ? i - 1 : i;
break;
}
}
this.radius = pivot;
// parition data into inside and outside
var iCount = buckets[pivot] - buckets[0];
var oCount = (n - buckets[pivot]) - buckets[0];
var iData = Array(iCount);
var oData = Array(oCount);
iCount = oCount = 0;
for(var i=0; i<n; i++) {
var v = data[i];
if(v === r) { continue; };
if(dist(r, v) <= pivot) {
iData[iCount++] = v;
} else {
oData[oCount++] = v;
}
}
// recursively create the rest of the tree
if(iCount > 0) {
this.inside = new VPTree(iData);
}
if(oCount > 0) {
this.outside = new VPTree(oData);
}
return this;
}
VPTree.prototype.query = function(value, distance, result) {
if(result === undefined) {
return this.query(value, distance, []);
}
// leaf node, test all values
if(this.values !== null) {
for(var i=0; i<this.values.length; i++) {
var v = this.values[i];
if(dist(value, v) <= distance) {
result.push(v);
}
}
return result;
}
// recursively test the rest of the tree
var tmpDistance = dist(value, this.center);
// inside
if(tmpDistance <= distance + this.radius) {
if(tmpDistance <= distance) {
result.push(this.center);
}
if(this.inside !== null) {
this.inside.query(value, distance, result);
}
}
// outside
if(tmpDistance + distance > this.radius && this.outside !== null) {
this.outside.query(value, distance, result);
}
return result;
}
EDIT Here's the JSFiddle showing a 2d (x, y) (8bits, 8bits) http://jsfiddle.net/fgq1rfLk/1/
If the points have explicit coordinates and if d is not too large, which seems to be the case here, I think (but I may be wrong, needs testing) that a Kd-tree will be more efficient than a VP-tree, since it can benefit from more structure from the data (coordinates), whereas VP-tree only "sees" point-to-point distances.
There is an efficient implementation of Kd-trees with all the needed range search functions (L2 and Manathan metric) in ANN [1] (however, it stores all coordinates explicitly and you probably want to benefit from your "compressed coordinates" representation.
An alternative is my own implementation of KdTree in Geogram [2], it is quite simple (though highly inspired by ANN) and can probably quite easily be adapted to use your compressed coordinates representation (but it only has k nearest neighbors search with L2 metric)
Referencecs:
[1] https://www.cs.umd.edu/~mount/ANN/
[2] http://alice.loria.fr/software/geogram/doc/html/classGEO_1_1KdTree.html