function cmp_with_indices(cmp)
{
return function(a, b) {
var res = cmp(a[0], b[0]);
return 0 > res ? -1 : (0 < res ? 1 : (a[1] - b[1]));
};
}
function with_indices(arr)
{
var n = arr.length, vi = new Array(n), i;
for (i=0; i<n; ++i) vi[i] = [arr[i], i];
return vi;
}
function get_indices(vi, inv)
{
var n = vi.length, i = new Array(n), j;
if (inv)
{
for (j=0; j<n; ++j) i[vi[j][1]] = j;
}
else
{
for (j=0; j<n; ++j) i[j] = vi[j][1];
}
return i;
}
function cmp(a, b)
{
return a < b ? -1 : (a > b ? 1 : 0);
}
function dist(a, b)
{
return Math.abs(a - b);
}
function align(A, B, dist_AB, cmp_AA, cmp_BB)
{
var n = A.length, m = B.length, i, j, k, s, sm, sM, km, jm, perm_A, perm_B, iperm_A, alignment;
if (n && m)
{
// O(NlogN), N = max(n,m)
// assume that cmp_AA, cmp_BB, dist_AB are "compatible" in that:
// (0 > cmp_AA(A, A') and 0 > cmp_BB(B, B')) ==> (dist_AB(A, B) + dist_AB(A', B')) <= (dist_AB(A', B) + dist_AB(A, B'))
// in other words, minimum overall distance is when alignment implies similarly sorted sequences
dist_AB = dist_AB || dist;
perm_A = get_indices(with_indices(A).sort(cmp_with_indices(cmp_AA || cmp)));
perm_B = get_indices(with_indices(B).sort(cmp_with_indices(cmp_BB || cmp)));
alignment = new Array(n);
if (n > m)
{
iperm_A = new Array(n);
for (i=0; i<n; ++i)
{
iperm_A[perm_A[i]] = i;
}
sm = Infinity; km = 0;
for (k=0; k+m<=n; ++k)
{
s = 0;
for (i=0; i<m; ++i) s += dist_AB(A[perm_A[i+k]], B[perm_B[i]]);
if (s < sm) {sm = s; km = k;}
}
for (i=0; i<m; ++i)
{
alignment[perm_A[i+km]] = perm_B[i];
}
for (i=0; i<n; ++i)
{
if (null == alignment[i])
{
// pad/interpolate
k = iperm_A[i]-km;
j = k<=0 ? 0 : (k>=m ? (m-1) : k);
s = dist_AB(A[i], B[perm_B[j]]);
sm = j-1>=0 ? dist_AB(A[i], B[perm_B[j-1]]) : Infinity;
sM = j+1<m ? dist_AB(A[i], B[perm_B[j+1]]) : Infinity;
jm = j;
if (sm < s)
{
s = sm;
jm = j-1;
}
if (sM < s)
{
s = sM;
jm = j+1;
}
alignment[i] = perm_B[jm];
}
}
}
else if (n < m)
{
sm = Infinity; km = 0;
for (k=0; k+n<=m; ++k)
{
s = 0;
for (i=0; i<n; ++i) s += dist_AB(A[perm_A[i]], B[perm_B[i+k]]);
if (s < sm) {sm = s; km = k;}
}
for (i=0; i<n; ++i)
{
alignment[perm_A[i]] = perm_B[i+km];
}
}
else// if (n === m)
{
for (i=0; i<n; ++i)
{
alignment[perm_A[i]] = perm_B[i];
}
}
return alignment;
}
return [];
}
function test(a, b, dist)
{
console.log('a:'+a.join(',')+' b:'+b.join(',')+' alignment:'+align(a, b, dist).join(','));
}
test([0, 1, 2], [0, 1, 2], dist);
test([0, 1, 2], [2, 1, 0], dist);
test([2, 1, 0], [0, 1, 2], dist);
test([2, 1, 0], [2, 1, 0], dist);
test([0, 1, 2], [0, 1], dist);
test([0, 1, 2], [1, 0], dist);
test([2, 1, 0], [0, 1], dist);
test([2, 1, 0], [1, 0], dist);
test([2, 1, 0, 3, 4], [1, 0, 2], dist);
test([2, 3, 1, 4, 0], [1, 3, 2], dist);
test([-1, 2, 3, 1, 4, 0, 5], [1, 3, 2], dist);
test([3, 4, 2, 1, 0], [1, 2], dist);
test([2, 1, 0, 3, 4], [0, 2], dist);
test([0, 1, 2], [-2, -1, 0, 1, 2], dist);
test([0, 1, 2], [2, 1, 0, 4, 3], dist);
test([2, 1, 0], [-2, -1, 0, 1, 2], dist);
test([2, 1, 0], [2, 1, 0, 4, 3], dist);
test([2, 1, 0], [0, 2, 4, 1, 3], dist);
Pythagoras
and find which combination provides the shortest distance. So you are basically building a tree of all possible combinations, for this you would need to consider what is it you are after, degree of precision or if it needs to be exact, how many results you want (can be many), speed [of the algorithm]? Having this in mind what you could do to speed up such process is to trim your tree; again how exact does it need to be? – Saimon