int length = a.length();
double numerator = 0;
// For both a and b, keep track of how many times each position i tied
// with some other position for rank.
SparseIntegerVector tiesInA = new CompactSparseIntegerVector(length);
SparseIntegerVector tiesInB = new CompactSparseIntegerVector(length);
boolean foundTies = false;
int concordant = 0;
int discordant = 0;
// For all pairs, track how many pairs satisfy the ordering
for (int i = 0; i < length; ++i) {
for (int j = i+1; j < length; ++j) {
// NOTE: this value will be 1 if there exists an match or
// "concordance" in the ordering of the two pairs. Otherwise
// it, will be a -1 of the pairs are not matched or are
// "discordant.
double ai = a.get(i);
double aj = a.get(j);
double bi = b.get(i);
double bj = b.get(j);
// Check for ties
boolean atie = ai == aj;
if (ai == aj) {
tiesInA.add(i, 1);
foundTies = true;
}
if (bi == bj) {
tiesInB.add(i, 1);
foundTies = true;
}
// If there was a tied rank, don't count the comparisons towards
// the concordance totals
if (ai != aj && bi != bj) {
if ((ai < aj && bi < bj) || (ai > aj && bi > bj))
concordant++;
else
discordant++;
}
}
}
int n = concordant - discordant;
double d = (.5 * (length * (length-1)));
if (foundTies) {
// IMPORTANT NOTE: for the summations, add 1 to the number of ties,
// rather than subtract 1. All the online pseudo code has (ties *
// (ties - 1)) / 2, which assumes that for a tied rank, ties will
// always have a value of 2 or more. I think they're double
// counting ties somehow, so we add 1 to account for this. Most
// importantly, adding 1 causes all the online Kendall's tau
// calculators to agree with our result.
double aSum = 0;
for (int i : tiesInA.getNonZeroIndices()) {
int ties = tiesInA.get(i);
aSum += (ties * (ties + 1) * .5);
}
double bSum = 0;
for (int i : tiesInB.getNonZeroIndices()) {
int ties = tiesInB.get(i);
bSum += (ties * (ties + 1) * .5);
}
return n / Math.sqrt((d - aSum) * (d - bSum));
}