The arithmetic-geometric mean of two numbers is defined as the limit of a sequence of arithmetic and geometric means.
Here is what I have for one iteration with two numbers:
function agm2(n)
{
/**
* @param {Float64Array(2)} n
*/
var x0 = n[0], x1 = n[1];
var a0 = (x0 + x1) / 2;
var a1 = Math.sqrt(x0 * x1);
return new Float64Array([a0, a1]);
}
Per this thread on MathOverflowDotNet, we can generalise this function to get the iteration-by-iteration function for an arbitrary number using elementary symmetic polynomials and binomial coefficients.
For three variables:
function agm3(n)
{
/**
* @param {Float64Array(3)} n
*/
var x0 = n[0], x1 = n[1], x2 = n[2];
var a0 = (x0 + x1 + x2) / 3;
var a1 = Math.sqrt((x0 * x1 + x1 * x2 + x0 * x2) / 3);
var a2 = Math.pow(x0 * x1 * x2, 1 / 3);
return new Float64Array([a0, a1, a2]);
}
Four variables:
function agm4(n)
{
/**
* @param {Float64Array(4)} n
*/
var x0 = n[0], x1 = n[1], x2 = n[2], x3 = n[3];
var a0 = (x0 + x1 + x2 + x3) / 4;
var a1 = Math.sqrt((
x0 * x1
+ x0 * x2
+ x0 * x3
+ x1 * x2
+ x1 * x3
+ x2 * x3
) / 6);
var a2 = Math.pow((
x0 * x1 * x2
+ x0 * x1 * x3
+ x0 * x2 * x3
+ x1 * x2 * x3
) / 4, 1 / 3);
var a3 = Math.pow(x0 * x1 * x2 * x3, 1 / 4);
return new Float64Array([a0, a1, a2, a3]);
}
And five variables:
function agm5(n)
{
/**
* @param {Float64Array(5)} n
*/
var x0 = n[0], x1 = n[1], x2 = n[2], x3 = n[3], x4 = n[4];
var a0 = (x0 + x1 + x2 + x3 + x4) / 5;
var a1 = Math.sqrt((
x0 * x1
+ x0 * x2
+ x0 * x3
+ x0 * x4
+ x1 * x2
+ x1 * x3
+ x1 * x4
+ x2 * x3
+ x2 * x4
+ x3 * x4
) / 10);
var a2 = Math.pow((
x0 * x1 * x2
+ x0 * x1 * x3
+ x0 * x1 * x4
+ x0 * x2 * x3
+ x0 * x2 * x4
+ x0 * x3 * x4
+ x1 * x2 * x3
+ x1 * x2 * x4
+ x1 * x3 * x4
+ x2 * x3 * x4
) / 10, 1 / 3);
var a3 = Math.pow((
x0 * x1 * x2 * x3
+ x0 * x1 * x2 * x4
+ x0 * x1 * x3 * x4
+ x0 * x2 * x3 * x4
+ x1 * x2 * x3 * x4
) / 5, 1 / 4);
var a4 = Math.pow(x0 * x1 * x2 * x3 * x4, 1 / 5);
return new Float64Array([a0, a1, a2, a3, a4]);
}
And six variables:
function agm6(n)
{
/**
* @param {Float64Array(5)} n
*/
var x0 = n[0], x1 = n[1], x2 = n[2], x3 = n[3], x4 = n[4], x5 = n[5];
var a0 = (x0 + x1 + x2 + x3 + x4 + x5) / 6;
var a1 = Math.sqrt((
x0 * x1
+ x0 * x2
+ x0 * x3
+ x0 * x4
+ x0 * x5
+ x1 * x2
+ x1 * x3
+ x1 * x4
+ x1 * x5
+ x2 * x3
+ x2 * x4
+ x2 * x5
+ x3 * x4
+ x3 * x5
+ x4 * x5
) / 15);
var a2 = Math.pow((
x0 * x1 * x2
+ x0 * x1 * x3
+ x0 * x1 * x4
+ x0 * x1 * x5
+ x0 * x2 * x3
+ x0 * x2 * x4
+ x0 * x2 * x5
+ x0 * x3 * x4
+ x0 * x3 * x5
+ x0 * x4 * x5
+ x1 * x2 * x3
+ x1 * x2 * x4
+ x1 * x2 * x5
+ x1 * x3 * x4
+ x1 * x3 * x5
+ x1 * x4 * x5
+ x2 * x3 * x4
+ x2 * x3 * x5
+ x2 * x4 * x5
+ x3 * x4 * x5
) / 20, 1 / 3);
var a3 = Math.pow((
x0 * x1 * x2 * x3
+ x0 * x1 * x2 * x4
+ x0 * x1 * x2 * x5
+ x0 * x1 * x3 * x4
+ x0 * x1 * x3 * x5
+ x0 * x1 * x4 * x5
+ x0 * x2 * x3 * x4
+ x0 * x2 * x3 * x5
+ x0 * x2 * x4 * x5
+ x0 * x3 * x4 * x5
+ x1 * x2 * x3 * x4
+ x1 * x2 * x3 * x5
+ x1 * x2 * x4 * x5
+ x1 * x3 * x4 * x5
+ x2 * x3 * x4 * x5
) / 15, 1 / 4);
var a4 = Math.pow((
x0 * x1 * x2 * x3 * x4
+ x0 * x1 * x2 * x3 * x5
+ x0 * x1 * x2 * x4 * x5
+ x0 * x1 * x3 * x4 * x5
+ x0 * x2 * x3 * x4 * x5
+ x1 * x2 * x3 * x4 * x5
) / 6, 1 / 5);
var a5 = Math.pow(x0 * x1 * x2 * x3 * x4 * x5, 1 / 6);
return new Float64Array([a0, a1, a2, a3, a4, a5]);
}
If you run this over and over again for any set of strictly positive real numbers, eventually all of the coefficients will tend towards a single value.
My question is, how can I generalise this so that I don’t have to define separate functions for each length?
The agm
function should take a Float64Array
of arbitrary length, and then output a Float64Array
with the correct values for one iteration.
Implementing binomial coefficients is no problem since I know how to write the factorial function for non-negative whole numbers, but how do I properly implement the elementary symmetric polynomials for an arbitrary array length beyond the first and last terms?
2
Answers
First, we define the binomial coefficient over non-negative whole numbers:
Then, using the code that gog kindly provided, I got:
I’m not sure I’ve got the math right, but it appears you can proceed like this:
Generate all integers from 1 to 2^n. For each integer, check which bits are set, multiply corresponding elements and place the result into the k-th bin, where k is the number of set bits (basically, you’re generating a powerset here).
Once complete, compute an average of each bin.
Example (using string concatenation instead of multiplication for clarity):