Simplify Horner's Method code and add the link to it in main READMe.

This commit is contained in:
Oleksii Trekhleb 2020-12-08 09:52:37 +01:00
parent fb6a1fae0a
commit 21400e36fc
6 changed files with 68 additions and 32 deletions

View File

@ -73,6 +73,7 @@ a set of rules that precisely define a sequence of operations.
* `B` [Complex Number](src/algorithms/math/complex-number) - complex numbers and basic operations with them * `B` [Complex Number](src/algorithms/math/complex-number) - complex numbers and basic operations with them
* `B` [Radian & Degree](src/algorithms/math/radian) - radians to degree and backwards conversion * `B` [Radian & Degree](src/algorithms/math/radian) - radians to degree and backwards conversion
* `B` [Fast Powering](src/algorithms/math/fast-powering) * `B` [Fast Powering](src/algorithms/math/fast-powering)
* `B` [Horner's method](src/algorithms/math/horner-method) - polynomial evaluation
* `A` [Integer Partition](src/algorithms/math/integer-partition) * `A` [Integer Partition](src/algorithms/math/integer-partition)
* `A` [Square Root](src/algorithms/math/square-root) - Newton's method * `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` [Liu Hui π Algorithm](src/algorithms/math/liu-hui) - approximate π calculations based on N-gons

View File

@ -1,21 +1,20 @@
# Horner's Method # Horner's Method
In mathematics, Horner's method (or Horner's scheme) is an algorithm for polynomial evaluation. In mathematics, Horner's method (or Horner's scheme) is an algorithm for polynomial evaluation. With this method, it is possible to evaluate a polynomial with only `n` additions and `n` multiplications. Hence, its storage requirements are `n` times the number of bits of `x`.
With this method, it is possible to evaluate a polynomial with only n additions and n multiplications.
Hence, its storage requirements are n times the number of bits of x.
Horner's method can be based on the following identity: Horner's method can be based on the following identity:
![](https://wikimedia.org/api/rest_v1/media/math/render/svg/2a576e42d875496f8b0f0dda5ebff7c2415532e4)
, which is called Horner's rule.
To solve the right part of the identity above, for a given x, we start by iterating through the polynomial from the inside out, ![Horner's rule](https://wikimedia.org/api/rest_v1/media/math/render/svg/2a576e42d875496f8b0f0dda5ebff7c2415532e4)
accumulating each iteration result. After n iterations, with n being the order of the polynomial, the accumulated result gives
us the polynomial evaluation.
Using the polynomial: This identity is called _Horner's rule_.
![](http://www.sciweavers.org/tex2img.php?eq=%244x%5E4%20%2B%202x%5E3%20%2B%203x%5E2%2B%20x%5E1%20%2B%203%24&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0), a traditional approach to evaluate it at x = 2, could be representing it as an array [3,1,3,2,4] and iterate over it saving each iteration value at an accumulator, such as acc += pow(x=2,index) * array[index]. In essence, each power of a number (pow) operation is n-1 multiplications. So, in this scenario, a total of 15 operations would have happened, composed of 5 additions, 5 multiplications, and 5 pows.
To solve the right part of the identity above, for a given `x`, we start by iterating through the polynomial from the inside out, accumulating each iteration result. After `n` iterations, with `n` being the order of the polynomial, the accumulated result gives us the polynomial evaluation.
**Using the polynomial:**
![Traditional approach](http://www.sciweavers.org/tex2img.php?eq=%244x%5E4%20%2B%202x%5E3%20%2B%203x%5E2%2B%20x%5E1%20%2B%203%24&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0), a traditional approach to evaluate it at `x = 2`, could be representing it as an array `[3, 1, 3, 2, 4]` and iterate over it saving each iteration value at an accumulator, such as `acc += pow(x=2, index) * array[index]`. In essence, each power of a number (`pow`) operation is `n-1` multiplications. So, in this scenario, a total of `14` operations would have happened, composed of `4` additions, `5` multiplications, and `5` pows (we're assuming that each power is calculated by repeated multiplication).
Now, **using the same scenario but with Horner's rule**, the polynomial can be re-written as ![Horner's rule approach](http://www.sciweavers.org/tex2img.php?eq=%24x%28x%28x%284x%2B2%29%2B3%29%2B1%29%2B3%24&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0), representing it as `[4, 2, 3, 1, 3]` it is possible to save the first iteration as `acc = arr[0] * (x=2) + arr[1]`, and then finish iterations for `acc *= (x=2) + arr[index]`. In the same scenario but using Horner's rule, a total of `10` operations would have happened, composed of only `4` additions and `4` multiplications.
Now, using the same scenario but with Horner's rule, the polynomial can be re-written as ![](http://www.sciweavers.org/tex2img.php?eq=%24x%28x%28x%284x%2B2%29%2B3%29%2B1%29%2B3%24&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0), representing it as [4,2,3,1,3] it is possible to save the first iteration as acc = arr[0]*(x=2) + arr[1], and then finish iterations for acc *= (x=2) + arr[index]. In the same scenario but using Horner's rule, a total of 10 operations would have happened, composed of only 5 additions and 5 multiplications.
## References ## References
- [Wikipedia](https://en.wikipedia.org/wiki/Horner%27s_method) - [Wikipedia](https://en.wikipedia.org/wiki/Horner%27s_method)

View File

@ -0,0 +1,14 @@
import classicPolynome from '../classicPolynome';
describe('classicPolynome', () => {
it('should evaluate the polynomial for the specified value of x correctly', () => {
expect(classicPolynome([8], 0.1)).toBe(8);
expect(classicPolynome([2, 4, 2, 5], 0.555)).toBe(7.68400775);
expect(classicPolynome([2, 4, 2, 5], 0.75)).toBe(9.59375);
expect(classicPolynome([1, 1, 1, 1, 1], 1.75)).toBe(20.55078125);
expect(classicPolynome([15, 3.5, 0, 2, 1.42, 0.41], 0.315)).toBe(1.1367300651406251);
expect(classicPolynome([0, 0, 2.77, 1.42, 0.41], 1.35)).toBe(7.375325000000001);
expect(classicPolynome([0, 0, 2.77, 1.42, 2.3311], 1.35)).toBe(9.296425000000001);
expect(classicPolynome([2, 0, 0, 5.757, 5.31412, 12.3213], 3.141)).toBe(697.2731167035034);
});
});

View File

@ -1,14 +1,21 @@
import hornerMethod from '../hornerMethod'; import hornerMethod from '../hornerMethod';
import classicPolynome from '../classicPolynome';
describe('hornerMethod', () => { describe('hornerMethod', () => {
it('should evaluate the polynomial on the specified point correctly', () => { it('should evaluate the polynomial for the specified value of x correctly', () => {
expect(hornerMethod([8],0.1)).toBe(8); expect(hornerMethod([8], 0.1)).toBe(8);
expect(hornerMethod([2,4,2,5],0.555)).toBe(7.68400775); expect(hornerMethod([2, 4, 2, 5], 0.555)).toBe(7.68400775);
expect(hornerMethod([2,4,2,5],0.75)).toBe(9.59375); expect(hornerMethod([2, 4, 2, 5], 0.75)).toBe(9.59375);
expect(hornerMethod([1,1,1,1,1],1.75)).toBe(20.55078125); expect(hornerMethod([1, 1, 1, 1, 1], 1.75)).toBe(20.55078125);
expect(hornerMethod([15,3.5,0,2,1.42,0.41],0.315)).toBe(1.136730065140625); expect(hornerMethod([15, 3.5, 0, 2, 1.42, 0.41], 0.315)).toBe(1.136730065140625);
expect(hornerMethod([0,0,2.77,1.42,0.41],1.35)).toBe(7.375325000000001); expect(hornerMethod([0, 0, 2.77, 1.42, 0.41], 1.35)).toBe(7.375325000000001);
expect(hornerMethod([0,0,2.77,1.42,2.3311],1.35)).toBe(9.296425000000001); expect(hornerMethod([0, 0, 2.77, 1.42, 2.3311], 1.35)).toBe(9.296425000000001);
expect(hornerMethod([2,0,0,5.757,5.31412,12.3213],3.141)).toBe(697.2731167035034); expect(hornerMethod([2, 0, 0, 5.757, 5.31412, 12.3213], 3.141)).toBe(697.2731167035034);
}); });
});
it('should evaluate the same polynomial value as classical approach', () => {
expect(hornerMethod([8], 0.1)).toBe(classicPolynome([8], 0.1));
expect(hornerMethod([2, 4, 2, 5], 0.555)).toBe(classicPolynome([2, 4, 2, 5], 0.555));
expect(hornerMethod([2, 4, 2, 5], 0.75)).toBe(classicPolynome([2, 4, 2, 5], 0.75));
});
});

View File

@ -0,0 +1,16 @@
/**
* Returns the evaluation of a polynomial function at a certain point.
* Uses straightforward approach with powers.
*
* @param {number[]} coefficients - i.e. [4, 3, 2] for (4 * x^2 + 3 * x + 2)
* @param {number} xVal
* @return {number}
*/
export default function classicPolynome(coefficients, xVal) {
return coefficients.reverse().reduce(
(accumulator, currentCoefficient, index) => {
return accumulator + currentCoefficient * (xVal ** index);
},
0,
);
}

View File

@ -1,17 +1,16 @@
/** /**
* Returns the evaluation of a polynomial function at a certain point. * Returns the evaluation of a polynomial function at a certain point.
* Uses Horner's rule. * Uses Horner's rule.
* @param {number[]} numbers *
* @param {number[]} coefficients - i.e. [4, 3, 2] for (4 * x^2 + 3 * x + 2)
* @param {number} xVal
* @return {number} * @return {number}
*/ */
export default function hornerMethod(numbers, point) { export default function hornerMethod(coefficients, xVal) {
// polynomial function is just a constant. return coefficients.reduce(
if (numbers.length === 1) { (accumulator, currentCoefficient) => {
return numbers[0]; return accumulator * xVal + currentCoefficient;
} },
return numbers.reduce((accumulator, currentValue, index) => { 0,
return index === 1 );
? numbers[0] * point + currentValue
: accumulator * point + currentValue;
});
} }