-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Algorithm to calculate the Arithmetic Geometric Mean #897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
dd9481e
Create ArithmeticGeometricMean.js
Rudxain 2cf398a
Merge branch 'TheAlgorithms:master' into patch-1
Rudxain 6ab3f4b
Finally added the test script for AGM
Rudxain 6ab7a98
Better doc, and corrected some formatting
Rudxain df68055
Merge branch 'TheAlgorithms:master' into patch-1
Rudxain 1dca4e6
Fixed syntax typos
Rudxain f1382e3
Added more tests and made FP comparison more "loose"
Rudxain 1762ac9
Patched bugs
Rudxain 0b812e9
Fixed-0 bug
Rudxain 3c8e162
Again, tried to fix minus zero
Rudxain f9d28a8
Finally fixed all bugs (probably)
Rudxain f81549d
Fixed style (probably)
Rudxain b0b0776
Fixed style
Rudxain 4f7431c
Fixed all style
Rudxain File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,28 @@ | ||
/** | ||
* @function agm | ||
* @description This finds the Arithmetic-Geometric Mean between any 2 numbers. | ||
* @param {Number} a - 1st number, also used to store Arithmetic Mean. | ||
* @param {Number} g - 2nd number, also used to store Geometric Mean. | ||
* @return {Number} - AGM of both numbers. | ||
* @see [AGM](https://en.wikipedia.org/wiki/Arithmetic%E2%80%93geometric_mean) | ||
*/ | ||
|
||
export const agm = (a, g) => { | ||
if (a === Infinity && g === 0) return NaN | ||
if (Object.is(a, -0) && !Object.is(g, -0)) return 0 | ||
if (a === g) return a // avoid rounding errors, and increase efficiency | ||
let x // temp var | ||
do { | ||
[a, g, x] = [(a + g) / 2, Math.sqrt(a * g), a] | ||
} while (a !== x && !isNaN(a)) | ||
/* | ||
`x !== a` ensures the return value has full precision, | ||
and prevents infinite loops caused by rounding differences between `div` and `sqrt` (no need for "epsilon"). | ||
If we were to compare `a` with `g`, some input combinations (not all) can cause an infinite loop, | ||
because the rounding mode never changes at runtime. | ||
Precision is not the same as accuracy, but they're related. | ||
This function isn't always 100% accurate (round-errors), but at least is more than 95% accurate. | ||
`!isNaN(x)` prevents infinite loops caused by invalid inputs like: negatives, NaNs and Infinities. | ||
*/ | ||
return a | ||
} |
This file contains hidden or 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,56 @@ | ||
import { agm } from '../ArithmeticGeometricMean.js' | ||
|
||
describe('Tests for AGM', () => { | ||
it('should be a function', () => { | ||
expect(typeof agm).toEqual('function') | ||
}) | ||
|
||
it('number of parameters should be 2', () => { | ||
expect(agm.length).toEqual(2) | ||
}) | ||
|
||
const m = 0x100 // scale for rand | ||
|
||
it('should return NaN if any or all params has a negative argument', () => { | ||
// I multiplied by minus one, because the sign inversion is more clearly visible | ||
expect(agm(-1 * Math.random() * m, Math.random() * m)).toBe(NaN) | ||
expect(agm(Math.random() * m, -1 * Math.random() * m)).toBe(NaN) | ||
expect(agm(-1 * Math.random() * m, -1 * Math.random() * m)).toBe(NaN) | ||
}) | ||
|
||
it('should return Infinity if any arg is Infinity and the other is not 0', () => { | ||
expect(agm(Math.random() * m + 1, Infinity)).toEqual(Infinity) | ||
expect(agm(Infinity, Math.random() * m + 1)).toEqual(Infinity) | ||
expect(agm(Infinity, Infinity)).toEqual(Infinity) | ||
}) | ||
|
||
it('should return NaN if some arg is Infinity and the other is 0', () => { | ||
expect(agm(0, Infinity)).toBe(NaN) | ||
expect(agm(Infinity, 0)).toBe(NaN) | ||
}) | ||
|
||
it('should return +0 if any or all args are +0 or -0, and return -0 if all are -0', () => { | ||
expect(agm(Math.random() * m, 0)).toBe(0) | ||
expect(agm(0, Math.random() * m)).toBe(0) | ||
expect(agm(Math.random() * m, -0)).toBe(0) | ||
expect(agm(-0, Math.random() * m)).toBe(0) | ||
expect(agm(0, -0)).toBe(0) | ||
expect(agm(-0, 0)).toBe(0) | ||
expect(agm(-0, -0)).toBe(-0) | ||
}) | ||
|
||
it('should return NaN if any or all args are NaN', () => { | ||
expect(agm(Math.random() * m, NaN)).toBe(NaN) | ||
expect(agm(NaN, Math.random() * m)).toBe(NaN) | ||
expect(agm(NaN, NaN)).toBe(NaN) | ||
}) | ||
|
||
it('should return an accurate approximation of the AGM between 2 valid input args', () => { | ||
// all the constants are provided by WolframAlpha | ||
expect(agm(1, 2)).toBeCloseTo(1.4567910310469068) | ||
expect(agm(2, 256)).toBeCloseTo(64.45940719438667) | ||
expect(agm(55555, 34)).toBeCloseTo(9933.4047239552) | ||
// test "unsafe" numbers | ||
expect(agm(2 ** 48, 3 ** 27)).toBeCloseTo(88506556379265.7) | ||
}) | ||
}) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.