Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Manhattan Distance between two matrices and minimum manhattan distanc…
…e between list of points of d dimenstions
  • Loading branch information
Tarun2605 committed Aug 28, 2025
commit efba313424bc52c2dbaaa9d5c9395d8ab7d2d28c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { manhattanDistance } from '../manhattanDistance';

describe('manhattanDistance', () => {
it('should calculate Manhattan distance between vectors', () => {
expect(manhattanDistance([[1]], [[2]])).toEqual(1);
expect(manhattanDistance([[2]], [[1]])).toEqual(1);
expect(manhattanDistance([[5, 8]], [[7, 3]])).toEqual(7);
expect(manhattanDistance([[5], [8]], [[7], [3]])).toEqual(7);
expect(manhattanDistance([[8, 2, 6]], [[3, 5, 7]])).toEqual(9);
expect(manhattanDistance([[8], [2], [6]], [[3], [5], [7]])).toEqual(9);
expect(manhattanDistance([[[8]], [[2]], [[6]]], [[[3]], [[5]], [[7]]])).toEqual(9);
});

it('should throw an error in case if two matrices are of different shapes', () => {
expect(() => manhattanDistance([[1]], [[[2]]])).toThrowError(
'Matrices have different dimensions',
);

expect(() => manhattanDistance([[1]], [[2, 3]])).toThrowError(
'Matrices have different shapes',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import minimumManhattanDistance from '../minimumManhattanDistance';

describe('minimumManhattanDistance', () => {
it('should return 0 for an empty array or an array with a single point', () => {
expect(minimumManhattanDistance([])).toBe(0);
expect(minimumManhattanDistance([[1, 2]])).toBe(0);
});

it('should find the minimum distance for 2D points', () => {
const points = [
[1, 1],
[3, 3],
[1, 4],
];
// d([1,1], [3,3]) = |1-3|+|1-3| = 4
// d([1,1], [1,4]) = |1-1|+|1-4| = 3
// d([3,3], [1,4]) = |3-1|+|3-4| = 3
expect(minimumManhattanDistance(points)).toBe(3);
});

it('should find the minimum distance for 3D points', () => {
const points = [
[0, 0, 0],
[2, 2, 2],
[3, 3, 3],
[1, 5, 1],
];
// d([2,2,2], [3,3,3]) = |2-3|+|2-3|+|2-3| = 1+1+1 = 3
expect(minimumManhattanDistance(points)).toBe(3);
});

it('should return 0 if there are identical points', () => {
const points = [
[10, 20, 5],
[1, 1, 1],
[4, 8, 12],
[10, 20, 5],
];
expect(minimumManhattanDistance(points)).toBe(0);
});

it('should work correctly with negative coordinates', () => {
const points = [
[-1, -1],
[2, 2],
[-3, 3],
[2, 3],
];
// d([2,2], [2,3]) = |2-2|+|2-3| = 1
expect(minimumManhattanDistance(points)).toBe(1);
});

it('should handle a larger set of points', () => {
const points = [
[0, 0],
[100, 100],
[10, 10],
[90, 90],
[49, 50],
[50, 50],
];
// d([49,50], [50,50]) = |49-50|+|50-50| = 1
expect(minimumManhattanDistance(points)).toBe(1);
});
});
26 changes: 26 additions & 0 deletions src/algorithms/math/manhattan_distance/manhattanDistance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @typedef {import('../matrix/Matrix.js').Matrix} Matrix
*/

import * as mtrx from '../matrix/Matrix';

/**
* Calculates the manhattan distance between 2 matrices.
*
* @param {Matrix} a
* @param {Matrix} b
* @returns {number}
* @trows {Error}
*/
export const manhattanDistance = (a, b) => {
mtrx.validateSameShape(a, b);

let distanceTotal = 0;

mtrx.walk(a, (indices, aCellValue) => {
const bCellValue = mtrx.getCellAtIndex(b, indices);
distanceTotal += Math.abs(aCellValue - bCellValue);
});

return distanceTotal;
};
65 changes: 65 additions & 0 deletions src/algorithms/math/manhattan_distance/minimumManhattanDistance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Calculates the Manhattan distance between two points (arrays of numbers).
* @param {number[]} a - The first point.
* @param {number[]} b - The second point.
* @returns {number} The Manhattan distance.
*/
const manhattanDistance = (a, b) => {
let distance = 0;
for (let i = 0; i < a.length; i += 1) {
distance += Math.abs(a[i] - b[i]);
}
return distance;
};

/**
* Finds the minimum Manhattan distance between any two points in a set.
* This is an implementation of the algorithm for the closest pair problem in
* Manhattan distance, which has a time complexity of O(n * log(n) * 2^d),
* where n is the number of points and d is the number of dimensions.
*
* @param {number[][]} points - An array of points, where each point is an array of numbers.
* @returns {number} The minimum Manhattan distance found.
*/
const minimumManhattanDistance = (points) => {
if (!points || points.length < 2) {
return 0;
}

const n = points.length;
const d = points[0].length;
let minDistance = Infinity;

// Generate all 2^d sign patterns (orientations).
// We only need 2^(d-1) because d(p,q) is the same for a mask and its inverse.
const limit = 1 << (d - 1);
for (let mask = 0; mask < limit; mask += 1) {
// Compute projection values for the current orientation.
const projections = [];
for (let i = 0; i < n; i += 1) {
let projectedValue = 0;
for (let j = 0; j < d; j += 1) {
// Determine the sign for the current dimension based on the bitmask.
const sign = ((mask >> j) & 1) ? 1 : -1;
projectedValue += sign * points[i][j];
}
projections.push({ value: projectedValue, index: i });
}

// Sort points based on their projected values.
projections.sort((a, b) => a.value - b.value);

// Check consecutive pairs in the sorted list.
// The closest pair for this orientation will be adjacent after sorting.
for (let i = 0; i < n - 1; i += 1) {
const pIndex = projections[i].index;
const qIndex = projections[i + 1].index;
const distance = manhattanDistance(points[pIndex], points[qIndex]);
minDistance = Math.min(minDistance, distance);
}
}

return minDistance;
};

export default minimumManhattanDistance;