Skip to content

Commit 610f16f

Browse files
committed
Add Euclidean Distance algorithm.
1 parent 59666ac commit 610f16f

File tree

7 files changed

+97
-26
lines changed

7 files changed

+97
-26
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ a set of rules that precisely define a sequence of operations.
7878
* `B` [Fast Powering](src/algorithms/math/fast-powering)
7979
* `B` [Horner's method](src/algorithms/math/horner-method) - polynomial evaluation
8080
* `B` [Matrices](src/algorithms/math/matrix) - matrices and basic matrix operations (multiplication, transposition, etc.)
81+
* `B` [Euclidean Distance](src/algorithms/math/euclidean-distance) - distance between two points/vectors/matrices
8182
* `A` [Integer Partition](src/algorithms/math/integer-partition)
8283
* `A` [Square Root](src/algorithms/math/square-root) - Newton's method
8384
* `A` [Liu Hui π Algorithm](src/algorithms/math/liu-hui) - approximate π calculations based on N-gons
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Euclidean Distance
2+
3+
In mathematics, the **Euclidean distance** between two points in Euclidean space is the length of a line segment between the two points. It can be calculated from the Cartesian coordinates of the points using the Pythagorean theorem, therefore occasionally being called the Pythagorean distance.
4+
5+
![Euclidean distance between two points](https://upload.wikimedia.org/wikipedia/commons/5/55/Euclidean_distance_2d.svg)
6+
7+
## Distance formulas
8+
9+
### One dimension
10+
11+
The distance between any two points on the real line is the absolute value of the numerical difference of their coordinates
12+
13+
![One dimension formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/7d75418dbec9482dbcb70f9063ad66e9cf7b5db9)
14+
15+
### Two dimensions
16+
17+
![Two dimensions formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/9c0157084fd89f5f3d462efeedc47d3d7aa0b773)
18+
19+
### Higher dimensions
20+
21+
In three dimensions, for points given by their Cartesian coordinates, the distance is
22+
23+
![Three dimensions formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/d1d13a40a7b203b455ae6d4be8b3cce898bda625)
24+
25+
Example: the distance between the two points `(8,2,6)` and `(3,5,7)`:
26+
27+
![3-dimension example](https://www.mathsisfun.com/algebra/images/dist-2-points-3d.svg)
28+
29+
In general, for points given by Cartesian coordinates in `n`-dimensional Euclidean space, the distance is
30+
31+
![n-dimensional formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/a0ef4fe055b2a51b4cca43a05e5d1cd93f758dcc)
32+
33+
## References
34+
35+
- [Euclidean Distance on MathIsFun](https://www.mathsisfun.com/algebra/distance-2-points.html)
36+
- [Euclidean Distance on Wikipedia](https://en.wikipedia.org/wiki/Euclidean_distance)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import euclideanDistance from '../euclideanDistance';
2+
3+
describe('euclideanDistance', () => {
4+
it('should calculate euclidean distance between vectors', () => {
5+
expect(euclideanDistance([[1]], [[2]])).toEqual(1);
6+
expect(euclideanDistance([[2]], [[1]])).toEqual(1);
7+
expect(euclideanDistance([[5, 8]], [[7, 3]])).toEqual(5.39);
8+
expect(euclideanDistance([[5], [8]], [[7], [3]])).toEqual(5.39);
9+
expect(euclideanDistance([[8, 2, 6]], [[3, 5, 7]])).toEqual(5.92);
10+
expect(euclideanDistance([[8], [2], [6]], [[3], [5], [7]])).toEqual(5.92);
11+
expect(euclideanDistance([[[8]], [[2]], [[6]]], [[[3]], [[5]], [[7]]])).toEqual(5.92);
12+
});
13+
14+
it('should throw an error in case if two matrices are of different shapes', () => {
15+
expect(() => euclideanDistance([[1]], [[[2]]])).toThrowError(
16+
'Matrices have different dimensions',
17+
);
18+
19+
expect(() => euclideanDistance([[1]], [[2, 3]])).toThrowError(
20+
'Matrices have different shapes',
21+
);
22+
});
23+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @typedef {import('../matrix/Matrix.js').Matrix} Matrix
3+
*/
4+
5+
import * as mtrx from '../matrix/Matrix';
6+
7+
/**
8+
* Calculates the euclidean distance between 2 matrices.
9+
*
10+
* @param {Matrix} a
11+
* @param {Matrix} b
12+
* @returns {number}
13+
* @trows {Error}
14+
*/
15+
const euclideanDistance = (a, b) => {
16+
mtrx.validateSameShape(a, b);
17+
18+
let squaresTotal = 0;
19+
20+
mtrx.walk(a, (indices, aCellValue) => {
21+
const bCellValue = mtrx.getCellAtIndex(b, indices);
22+
squaresTotal += (aCellValue - bCellValue) ** 2;
23+
});
24+
25+
return Number(Math.sqrt(squaresTotal).toFixed(2));
26+
};
27+
28+
export default euclideanDistance;

src/algorithms/math/matrix/Matrix.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const validate2D = (m) => {
5858
* @param {Matrix} b
5959
* @trows {Error}
6060
*/
61-
const validateSameShape = (a, b) => {
61+
export const validateSameShape = (a, b) => {
6262
validateType(a);
6363
validateType(b);
6464

@@ -177,7 +177,7 @@ export const t = (m) => {
177177
* @param {Matrix} m
178178
* @param {function(indices: CellIndices, c: Cell)} visit
179179
*/
180-
const walk = (m, visit) => {
180+
export const walk = (m, visit) => {
181181
/**
182182
* Traverses the matrix recursively.
183183
*
@@ -208,7 +208,7 @@ const walk = (m, visit) => {
208208
* @param {CellIndices} cellIndices - Array of cell indices
209209
* @return {Cell}
210210
*/
211-
const getCellAtIndex = (m, cellIndices) => {
211+
export const getCellAtIndex = (m, cellIndices) => {
212212
// We start from the row at specific index.
213213
let cell = m[cellIndices[0]];
214214
// Going deeper into the next dimensions but not to the last one to preserve
@@ -227,7 +227,7 @@ const getCellAtIndex = (m, cellIndices) => {
227227
* @param {CellIndices} cellIndices - Array of cell indices
228228
* @param {Cell} cellValue - New cell value
229229
*/
230-
const updateCellAtIndex = (m, cellIndices, cellValue) => {
230+
export const updateCellAtIndex = (m, cellIndices, cellValue) => {
231231
// We start from the row at specific index.
232232
let cell = m[cellIndices[0]];
233233
// Going deeper into the next dimensions but not to the last one to preserve

src/algorithms/ml/knn/__test__/knn.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('kNN', () => {
2525
const inconsistent = () => {
2626
kNN([[1, 1]], [1], [1]);
2727
};
28-
expect(inconsistent).toThrowError('Inconsistent vector lengths');
28+
expect(inconsistent).toThrowError('Matrices have different shapes');
2929
});
3030

3131
it('should find the nearest neighbour', () => {

src/algorithms/ml/knn/kNN.js

+4-21
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,3 @@
1-
/**
2-
* Calculates calculate the euclidean distance between 2 vectors.
3-
*
4-
* @param {number[]} x1
5-
* @param {number[]} x2
6-
* @returns {number}
7-
*/
8-
function euclideanDistance(x1, x2) {
9-
// Checking for errors.
10-
if (x1.length !== x2.length) {
11-
throw new Error('Inconsistent vector lengths');
12-
}
13-
// Calculate the euclidean distance between 2 vectors and return.
14-
let squaresTotal = 0;
15-
for (let i = 0; i < x1.length; i += 1) {
16-
squaresTotal += (x1[i] - x2[i]) ** 2;
17-
}
18-
return Number(Math.sqrt(squaresTotal).toFixed(2));
19-
}
20-
211
/**
222
* Classifies the point in space based on k-nearest neighbors algorithm.
233
*
@@ -27,6 +7,9 @@ function euclideanDistance(x1, x2) {
277
* @param {number} k - number of nearest neighbors which will be taken into account (preferably odd)
288
* @return {number} - the class of the point
299
*/
10+
11+
import euclideanDistance from '../../math/euclidean-distance/euclideanDistance';
12+
3013
export default function kNN(
3114
dataSet,
3215
labels,
@@ -42,7 +25,7 @@ export default function kNN(
4225
const distances = [];
4326
for (let i = 0; i < dataSet.length; i += 1) {
4427
distances.push({
45-
dist: euclideanDistance(dataSet[i], toClassify),
28+
dist: euclideanDistance([dataSet[i]], [toClassify]),
4629
label: labels[i],
4730
});
4831
}

0 commit comments

Comments
 (0)