forked from trekhleb/javascript-algorithms
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 changes: 69 additions & 0 deletions
69
src/algorithms/math/square-root/__test__/squareRoot.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |