Skip to content

Commit

Permalink
Add square root finding algorithm.
Browse files Browse the repository at this point in the history
  • Loading branch information
trekhleb committed Mar 23, 2019
1 parent 4aecd57 commit 339ae02
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
62 changes: 62 additions & 0 deletions src/algorithms/math/square-root/README.md
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 `` is from where it needs to be, and the
division by `2x` is the derivative of ``, to scale how much we adjust `x` by how
quickly `` 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 src/algorithms/math/square-root/__test__/squareRoot.test.js
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);
});
});
40 changes: 40 additions & 0 deletions src/algorithms/math/square-root/squareRoot.js
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);
}

0 comments on commit 339ae02

Please sign in to comment.