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.
Simplify Horner's Method code and add the link to it in main READMe.
- Loading branch information
Showing
6 changed files
with
68 additions
and
32 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 |
---|---|---|
@@ -1,21 +1,20 @@ | ||
# Horner's Method | ||
|
||
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. | ||
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`. | ||
|
||
Horner's method can be based on the following identity: | ||
 | ||
, 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, | ||
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: | ||
, 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. | ||
This identity 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, 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:** | ||
, 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 , 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 , 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 | ||
|
||
- [Wikipedia](https://en.wikipedia.org/wiki/Horner%27s_method) |
14 changes: 14 additions & 0 deletions
14
src/algorithms/math/horner-method/__test__/classicPolynome.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,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); | ||
}); | ||
}); |
27 changes: 17 additions & 10 deletions
27
src/algorithms/math/horner-method/__test__/hornerMethod.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 |
---|---|---|
@@ -1,14 +1,21 @@ | ||
import hornerMethod from '../hornerMethod'; | ||
import classicPolynome from '../classicPolynome'; | ||
|
||
describe('hornerMethod', () => { | ||
it('should evaluate the polynomial on the specified point correctly', () => { | ||
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.75)).toBe(9.59375); | ||
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([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([2,0,0,5.757,5.31412,12.3213],3.141)).toBe(697.2731167035034); | ||
it('should evaluate the polynomial for the specified value of x correctly', () => { | ||
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.75)).toBe(9.59375); | ||
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([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([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)); | ||
}); | ||
}); |
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,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, | ||
); | ||
} |
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 |
---|---|---|
@@ -1,17 +1,16 @@ | ||
/** | ||
* Returns the evaluation of a polynomial function at a certain point. | ||
* 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} | ||
*/ | ||
export default function hornerMethod(numbers, point) { | ||
// polynomial function is just a constant. | ||
if (numbers.length === 1) { | ||
return numbers[0]; | ||
} | ||
return numbers.reduce((accumulator, currentValue, index) => { | ||
return index === 1 | ||
? numbers[0] * point + currentValue | ||
: accumulator * point + currentValue; | ||
}); | ||
export default function hornerMethod(coefficients, xVal) { | ||
return coefficients.reduce( | ||
(accumulator, currentCoefficient) => { | ||
return accumulator * xVal + currentCoefficient; | ||
}, | ||
0, | ||
); | ||
} |