mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-10 11:09:43 +08:00
Add Euclidean Distance algorithm.
This commit is contained in:
parent
59666ac947
commit
610f16fe20
@ -78,6 +78,7 @@ a set of rules that precisely define a sequence of operations.
|
||||
* `B` [Fast Powering](src/algorithms/math/fast-powering)
|
||||
* `B` [Horner's method](src/algorithms/math/horner-method) - polynomial evaluation
|
||||
* `B` [Matrices](src/algorithms/math/matrix) - matrices and basic matrix operations (multiplication, transposition, etc.)
|
||||
* `B` [Euclidean Distance](src/algorithms/math/euclidean-distance) - distance between two points/vectors/matrices
|
||||
* `A` [Integer Partition](src/algorithms/math/integer-partition)
|
||||
* `A` [Square Root](src/algorithms/math/square-root) - Newton's method
|
||||
* `A` [Liu Hui π Algorithm](src/algorithms/math/liu-hui) - approximate π calculations based on N-gons
|
||||
|
36
src/algorithms/math/euclidean-distance/README.md
Normal file
36
src/algorithms/math/euclidean-distance/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Euclidean Distance
|
||||
|
||||
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.
|
||||
|
||||
![Euclidean distance between two points](https://upload.wikimedia.org/wikipedia/commons/5/55/Euclidean_distance_2d.svg)
|
||||
|
||||
## Distance formulas
|
||||
|
||||
### One dimension
|
||||
|
||||
The distance between any two points on the real line is the absolute value of the numerical difference of their coordinates
|
||||
|
||||
![One dimension formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/7d75418dbec9482dbcb70f9063ad66e9cf7b5db9)
|
||||
|
||||
### Two dimensions
|
||||
|
||||
![Two dimensions formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/9c0157084fd89f5f3d462efeedc47d3d7aa0b773)
|
||||
|
||||
### Higher dimensions
|
||||
|
||||
In three dimensions, for points given by their Cartesian coordinates, the distance is
|
||||
|
||||
![Three dimensions formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/d1d13a40a7b203b455ae6d4be8b3cce898bda625)
|
||||
|
||||
Example: the distance between the two points `(8,2,6)` and `(3,5,7)`:
|
||||
|
||||
![3-dimension example](https://www.mathsisfun.com/algebra/images/dist-2-points-3d.svg)
|
||||
|
||||
In general, for points given by Cartesian coordinates in `n`-dimensional Euclidean space, the distance is
|
||||
|
||||
![n-dimensional formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/a0ef4fe055b2a51b4cca43a05e5d1cd93f758dcc)
|
||||
|
||||
## References
|
||||
|
||||
- [Euclidean Distance on MathIsFun](https://www.mathsisfun.com/algebra/distance-2-points.html)
|
||||
- [Euclidean Distance on Wikipedia](https://en.wikipedia.org/wiki/Euclidean_distance)
|
@ -0,0 +1,23 @@
|
||||
import euclideanDistance from '../euclideanDistance';
|
||||
|
||||
describe('euclideanDistance', () => {
|
||||
it('should calculate euclidean distance between vectors', () => {
|
||||
expect(euclideanDistance([[1]], [[2]])).toEqual(1);
|
||||
expect(euclideanDistance([[2]], [[1]])).toEqual(1);
|
||||
expect(euclideanDistance([[5, 8]], [[7, 3]])).toEqual(5.39);
|
||||
expect(euclideanDistance([[5], [8]], [[7], [3]])).toEqual(5.39);
|
||||
expect(euclideanDistance([[8, 2, 6]], [[3, 5, 7]])).toEqual(5.92);
|
||||
expect(euclideanDistance([[8], [2], [6]], [[3], [5], [7]])).toEqual(5.92);
|
||||
expect(euclideanDistance([[[8]], [[2]], [[6]]], [[[3]], [[5]], [[7]]])).toEqual(5.92);
|
||||
});
|
||||
|
||||
it('should throw an error in case if two matrices are of different shapes', () => {
|
||||
expect(() => euclideanDistance([[1]], [[[2]]])).toThrowError(
|
||||
'Matrices have different dimensions',
|
||||
);
|
||||
|
||||
expect(() => euclideanDistance([[1]], [[2, 3]])).toThrowError(
|
||||
'Matrices have different shapes',
|
||||
);
|
||||
});
|
||||
});
|
28
src/algorithms/math/euclidean-distance/euclideanDistance.js
Normal file
28
src/algorithms/math/euclidean-distance/euclideanDistance.js
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @typedef {import('../matrix/Matrix.js').Matrix} Matrix
|
||||
*/
|
||||
|
||||
import * as mtrx from '../matrix/Matrix';
|
||||
|
||||
/**
|
||||
* Calculates the euclidean distance between 2 matrices.
|
||||
*
|
||||
* @param {Matrix} a
|
||||
* @param {Matrix} b
|
||||
* @returns {number}
|
||||
* @trows {Error}
|
||||
*/
|
||||
const euclideanDistance = (a, b) => {
|
||||
mtrx.validateSameShape(a, b);
|
||||
|
||||
let squaresTotal = 0;
|
||||
|
||||
mtrx.walk(a, (indices, aCellValue) => {
|
||||
const bCellValue = mtrx.getCellAtIndex(b, indices);
|
||||
squaresTotal += (aCellValue - bCellValue) ** 2;
|
||||
});
|
||||
|
||||
return Number(Math.sqrt(squaresTotal).toFixed(2));
|
||||
};
|
||||
|
||||
export default euclideanDistance;
|
@ -58,7 +58,7 @@ const validate2D = (m) => {
|
||||
* @param {Matrix} b
|
||||
* @trows {Error}
|
||||
*/
|
||||
const validateSameShape = (a, b) => {
|
||||
export const validateSameShape = (a, b) => {
|
||||
validateType(a);
|
||||
validateType(b);
|
||||
|
||||
@ -177,7 +177,7 @@ export const t = (m) => {
|
||||
* @param {Matrix} m
|
||||
* @param {function(indices: CellIndices, c: Cell)} visit
|
||||
*/
|
||||
const walk = (m, visit) => {
|
||||
export const walk = (m, visit) => {
|
||||
/**
|
||||
* Traverses the matrix recursively.
|
||||
*
|
||||
@ -208,7 +208,7 @@ const walk = (m, visit) => {
|
||||
* @param {CellIndices} cellIndices - Array of cell indices
|
||||
* @return {Cell}
|
||||
*/
|
||||
const getCellAtIndex = (m, cellIndices) => {
|
||||
export const getCellAtIndex = (m, cellIndices) => {
|
||||
// We start from the row at specific index.
|
||||
let cell = m[cellIndices[0]];
|
||||
// Going deeper into the next dimensions but not to the last one to preserve
|
||||
@ -227,7 +227,7 @@ const getCellAtIndex = (m, cellIndices) => {
|
||||
* @param {CellIndices} cellIndices - Array of cell indices
|
||||
* @param {Cell} cellValue - New cell value
|
||||
*/
|
||||
const updateCellAtIndex = (m, cellIndices, cellValue) => {
|
||||
export const updateCellAtIndex = (m, cellIndices, cellValue) => {
|
||||
// We start from the row at specific index.
|
||||
let cell = m[cellIndices[0]];
|
||||
// Going deeper into the next dimensions but not to the last one to preserve
|
||||
|
@ -25,7 +25,7 @@ describe('kNN', () => {
|
||||
const inconsistent = () => {
|
||||
kNN([[1, 1]], [1], [1]);
|
||||
};
|
||||
expect(inconsistent).toThrowError('Inconsistent vector lengths');
|
||||
expect(inconsistent).toThrowError('Matrices have different shapes');
|
||||
});
|
||||
|
||||
it('should find the nearest neighbour', () => {
|
||||
|
@ -1,23 +1,3 @@
|
||||
/**
|
||||
* Calculates calculate the euclidean distance between 2 vectors.
|
||||
*
|
||||
* @param {number[]} x1
|
||||
* @param {number[]} x2
|
||||
* @returns {number}
|
||||
*/
|
||||
function euclideanDistance(x1, x2) {
|
||||
// Checking for errors.
|
||||
if (x1.length !== x2.length) {
|
||||
throw new Error('Inconsistent vector lengths');
|
||||
}
|
||||
// Calculate the euclidean distance between 2 vectors and return.
|
||||
let squaresTotal = 0;
|
||||
for (let i = 0; i < x1.length; i += 1) {
|
||||
squaresTotal += (x1[i] - x2[i]) ** 2;
|
||||
}
|
||||
return Number(Math.sqrt(squaresTotal).toFixed(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Classifies the point in space based on k-nearest neighbors algorithm.
|
||||
*
|
||||
@ -27,6 +7,9 @@ function euclideanDistance(x1, x2) {
|
||||
* @param {number} k - number of nearest neighbors which will be taken into account (preferably odd)
|
||||
* @return {number} - the class of the point
|
||||
*/
|
||||
|
||||
import euclideanDistance from '../../math/euclidean-distance/euclideanDistance';
|
||||
|
||||
export default function kNN(
|
||||
dataSet,
|
||||
labels,
|
||||
@ -42,7 +25,7 @@ export default function kNN(
|
||||
const distances = [];
|
||||
for (let i = 0; i < dataSet.length; i += 1) {
|
||||
distances.push({
|
||||
dist: euclideanDistance(dataSet[i], toClassify),
|
||||
dist: euclideanDistance([dataSet[i]], [toClassify]),
|
||||
label: labels[i],
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user