mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-10 11:09:43 +08:00
Add square root finding algorithm.
This commit is contained in:
parent
4aecd5772f
commit
339ae02977
@ -73,6 +73,7 @@ a set of rules that precisely define a sequence of operations.
|
||||
* `B` [Radian & Degree](src/algorithms/math/radian) - radians to degree and backwards conversion
|
||||
* `B` [Fast Powering](src/algorithms/math/fast-powering)
|
||||
* `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
|
||||
* `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - decompose a function of time (a signal) into the frequencies that make it up
|
||||
* **Sets**
|
||||
|
62
src/algorithms/math/square-root/README.md
Normal file
62
src/algorithms/math/square-root/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Square Root (Newton's Method)
|
||||
|
||||
In numerical analysis, a branch of mathematics, there are several square root
|
||||
algorithms or methods of computing the principal square root of a non-negative real
|
||||
number. As, generally, the roots of a function cannot be computed exactly.
|
||||
The root-finding algorithms provide approximations to roots expressed as floating
|
||||
point numbers.
|
||||
|
||||
Finding ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/bff86975b0e7944720b3e635c53c22c032a7a6f1) is
|
||||
the same as solving the equation ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6cf57722151ef19ba1ca918d702b95c335e21cad) for a
|
||||
positive `x`. Therefore, any general numerical root-finding algorithm can be used.
|
||||
|
||||
**Newton's method** (also known as the Newton–Raphson method), named after
|
||||
_Isaac Newton_ and _Joseph Raphson_, is one example of a root-finding algorithm. It is a
|
||||
method for finding successively better approximations to the roots of a real-valued function.
|
||||
|
||||
Let's start by explaining the general idea of Newton's method and then apply it to our particular
|
||||
case with finding a square root of the number.
|
||||
|
||||
## Newton's Method General Idea
|
||||
|
||||
The Newton–Raphson method in one variable is implemented as follows:
|
||||
|
||||
The method starts with a function `f` defined over the real numbers `x`, the function's derivative `f'`, and an
|
||||
initial guess `x0` for a root of the function `f`. If the function satisfies the assumptions made in the derivation
|
||||
of the formula and the initial guess is close, then a better approximation `x1` is:
|
||||
|
||||
![](https://wikimedia.org/api/rest_v1/media/math/render/svg/52c50eca0b7c4d64ef2fdca678665b73e944cb84)
|
||||
|
||||
Geometrically, `(x1, 0)` is the intersection of the `x`-axis and the tangent of
|
||||
the graph of `f` at `(x0, f (x0))`.
|
||||
|
||||
The process is repeated as:
|
||||
|
||||
![](https://wikimedia.org/api/rest_v1/media/math/render/svg/710c11b9ec4568d1cfff49b7c7d41e0a7829a736)
|
||||
|
||||
until a sufficiently accurate value is reached.
|
||||
|
||||
![](https://upload.wikimedia.org/wikipedia/commons/e/e0/NewtonIteration_Ani.gif)
|
||||
|
||||
## Newton's Method of Finding a Square Root
|
||||
|
||||
As it was mentioned above, finding ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/bff86975b0e7944720b3e635c53c22c032a7a6f1) is
|
||||
the same as solving the equation ![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6cf57722151ef19ba1ca918d702b95c335e21cad) for a
|
||||
positive `x`.
|
||||
|
||||
The derivative of the function `f(x)` in case of square root problem is `2x`.
|
||||
|
||||
After applying the Newton's formula (see above) we get the following equation for our algorithm iterations:
|
||||
|
||||
```text
|
||||
x := x - (x² - S) / (2x)
|
||||
```
|
||||
|
||||
The `x² − S` above is how far away `x²` is from where it needs to be, and the
|
||||
division by `2x` is the derivative of `x²`, to scale how much we adjust `x` by how
|
||||
quickly `x²` is changing.
|
||||
|
||||
## References
|
||||
|
||||
- [Methods of computing square roots on Wikipedia](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots)
|
||||
- [Newton's method on Wikipedia](https://en.wikipedia.org/wiki/Newton%27s_method)
|
69
src/algorithms/math/square-root/__test__/squareRoot.test.js
Normal file
69
src/algorithms/math/square-root/__test__/squareRoot.test.js
Normal file
@ -0,0 +1,69 @@
|
||||
import squareRoot from '../squareRoot';
|
||||
|
||||
describe('squareRoot', () => {
|
||||
it('should throw for negative numbers', () => {
|
||||
function failingSquareRoot() {
|
||||
squareRoot(-5);
|
||||
}
|
||||
expect(failingSquareRoot).toThrow();
|
||||
});
|
||||
|
||||
it('should correctly calculate square root with default tolerance', () => {
|
||||
expect(squareRoot(0)).toBe(0);
|
||||
expect(squareRoot(1)).toBe(1);
|
||||
expect(squareRoot(2)).toBe(1);
|
||||
expect(squareRoot(3)).toBe(2);
|
||||
expect(squareRoot(4)).toBe(2);
|
||||
expect(squareRoot(15)).toBe(4);
|
||||
expect(squareRoot(16)).toBe(4);
|
||||
expect(squareRoot(256)).toBe(16);
|
||||
expect(squareRoot(473)).toBe(22);
|
||||
expect(squareRoot(14723)).toBe(121);
|
||||
});
|
||||
|
||||
it('should correctly calculate square root for integers with custom tolerance', () => {
|
||||
let tolerance = 1;
|
||||
|
||||
expect(squareRoot(0, tolerance)).toBe(0);
|
||||
expect(squareRoot(1, tolerance)).toBe(1);
|
||||
expect(squareRoot(2, tolerance)).toBe(1.4);
|
||||
expect(squareRoot(3, tolerance)).toBe(1.8);
|
||||
expect(squareRoot(4, tolerance)).toBe(2);
|
||||
expect(squareRoot(15, tolerance)).toBe(3.9);
|
||||
expect(squareRoot(16, tolerance)).toBe(4);
|
||||
expect(squareRoot(256, tolerance)).toBe(16);
|
||||
expect(squareRoot(473, tolerance)).toBe(21.7);
|
||||
expect(squareRoot(14723, tolerance)).toBe(121.3);
|
||||
|
||||
tolerance = 3;
|
||||
|
||||
expect(squareRoot(0, tolerance)).toBe(0);
|
||||
expect(squareRoot(1, tolerance)).toBe(1);
|
||||
expect(squareRoot(2, tolerance)).toBe(1.414);
|
||||
expect(squareRoot(3, tolerance)).toBe(1.732);
|
||||
expect(squareRoot(4, tolerance)).toBe(2);
|
||||
expect(squareRoot(15, tolerance)).toBe(3.873);
|
||||
expect(squareRoot(16, tolerance)).toBe(4);
|
||||
expect(squareRoot(256, tolerance)).toBe(16);
|
||||
expect(squareRoot(473, tolerance)).toBe(21.749);
|
||||
expect(squareRoot(14723, tolerance)).toBe(121.338);
|
||||
|
||||
tolerance = 10;
|
||||
|
||||
expect(squareRoot(0, tolerance)).toBe(0);
|
||||
expect(squareRoot(1, tolerance)).toBe(1);
|
||||
expect(squareRoot(2, tolerance)).toBe(1.4142135624);
|
||||
expect(squareRoot(3, tolerance)).toBe(1.7320508076);
|
||||
expect(squareRoot(4, tolerance)).toBe(2);
|
||||
expect(squareRoot(15, tolerance)).toBe(3.8729833462);
|
||||
expect(squareRoot(16, tolerance)).toBe(4);
|
||||
expect(squareRoot(256, tolerance)).toBe(16);
|
||||
expect(squareRoot(473, tolerance)).toBe(21.7485631709);
|
||||
expect(squareRoot(14723, tolerance)).toBe(121.3383698588);
|
||||
});
|
||||
|
||||
it('should correctly calculate square root for integers with custom tolerance', () => {
|
||||
expect(squareRoot(4.5, 10)).toBe(2.1213203436);
|
||||
expect(squareRoot(217.534, 10)).toBe(14.7490338667);
|
||||
});
|
||||
});
|
40
src/algorithms/math/square-root/squareRoot.js
Normal file
40
src/algorithms/math/square-root/squareRoot.js
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Calculates the square root of the number with given tolerance (precision)
|
||||
* by using Newton's method.
|
||||
*
|
||||
* @param number - the number we want to find a square root for.
|
||||
* @param [tolerance] - how many precise numbers after the floating point we want to get.
|
||||
* @return {number}
|
||||
*/
|
||||
export default function squareRoot(number, tolerance = 0) {
|
||||
// For now we won't support operations that involves manipulation with complex numbers.
|
||||
if (number < 0) {
|
||||
throw new Error('The method supports only positive integers');
|
||||
}
|
||||
|
||||
// Handle edge case with finding the square root of zero.
|
||||
if (number === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We will start approximation from value 1.
|
||||
let root = 1;
|
||||
|
||||
// Delta is a desired distance between the number and the square of the root.
|
||||
// - if tolerance=0 then delta=1
|
||||
// - if tolerance=1 then delta=0.1
|
||||
// - if tolerance=2 then delta=0.01
|
||||
// - and so on...
|
||||
const requiredDelta = 1 / (10 ** tolerance);
|
||||
|
||||
// Approximating the root value to the point when we get a desired precision.
|
||||
while (Math.abs(number - (root ** 2)) > requiredDelta) {
|
||||
// Newton's method reduces in this case to the so-called Babylonian method.
|
||||
// These methods generally yield approximate results, but can be made arbitrarily
|
||||
// precise by increasing the number of calculation steps.
|
||||
root -= ((root ** 2) - number) / (2 * root);
|
||||
}
|
||||
|
||||
// Cut off undesired floating digits and return the root value.
|
||||
return Math.round(root * (10 ** tolerance)) / (10 ** tolerance);
|
||||
}
|
Loading…
Reference in New Issue
Block a user