I was interested in some variations of this problem, in a different context, and was thinking of solutions along similar lines as Ivo's answer.
So here I give a solution (in JavaScript) for the extended problem which seeks the alignment that minimizes total given distance metric between sequences and which can also handle sequences of unequal length.
Examples
note: alignment is like the permutation (and/or warping) of the indexes of (parts of) the b sequence, that minimizes total given distance metric with the a sequence
a:0,1,2 b:0,1,2 alignment:0,1,2 a:0,1,2 b:2,1,0 alignment:2,1,0 a:2,1,0 b:0,1,2 alignment:2,1,0 a:2,1,0 b:2,1,0 alignment:0,1,2 a:0,1,2 b:0,1 alignment:0,1,1 a:0,1,2 b:1,0 alignment:1,0,0 a:2,1,0 b:0,1 alignment:1,1,0 a:2,1,0 b:1,0 alignment:0,0,1 a:2,1,0,3,4 b:1,0,2 alignment:2,0,1,2,2 a:2,3,1,4,0 b:1,3,2 alignment:2,1,0,1,0 a:-1,2,3,1,4,0,5 b:1,3,2 alignment:0,2,1,0,1,0,1 a:3,4,2,1,0 b:1,2 alignment:1,1,1,0,0 a:2,1,0,3,4 b:0,2 alignment:1,1,0,1,1 a:0,1,2 b:-2,-1,0,1,2 alignment:2,3,4 a:0,1,2 b:2,1,0,4,3 alignment:2,1,0 a:2,1,0 b:-2,-1,0,1,2 alignment:4,3,2 a:2,1,0 b:2,1,0,4,3 alignment:0,1,2 a:2,1,0 b:0,2,4,1,3 alignment:1,3,0
JS Code:
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);
Pythagorasand 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?