package org.activityinfo.geoadmin; /** * Matches a set of n columns to the most similar m columns */ public class ColumnMatchMatrix { public static final double MIN_SCORE = 0d; private double scores[][]; private int counts[][]; private int rows; private int cols; public ColumnMatchMatrix(int rows, int cols) { this.rows = rows; this.cols = cols; scores = new double[rows][cols]; counts = new int[rows][cols]; } public void addScore(int row, int col, double score) { scores[row][col] += score; counts[row][col] ++; } /** * Optimal mapping from left to right columns */ public int[] solve() { normalizeScores(); // keep track of matched/unmatched columns int matchedRows[] = new int[rows]; boolean matchedCols[] = new boolean[cols]; // first, find the best, dominant match for each column in the left for(int i=0;i!=rows;++i) { int bestCol = findColMax(i, matchedCols); if(bestCol >= 0 && findRowMax(bestCol) == i) { matchedRows[i] = bestCol; matchedCols[bestCol] = true; } else { matchedRows[i] = -1; } } // now, find the best among what's left over // (pretty arbitrary order) for(int i=0;i!=rows;++i) { int bestCol = findColMax(i, matchedCols); if(bestCol >= 0) { matchedRows[i] = bestCol; matchedCols[bestCol] = true; } } return matchedRows; } private void normalizeScores() { for(int i=0;i!=rows;++i) { for (int j = 0; j < cols; ++j) { double count = counts[i][j]; if(count > 0) { scores[i][j] /= count; } } } } private int findColMax(int i, boolean[] matchedCols) { int maxCol = -1; double max = MIN_SCORE; for(int j=0;j<cols;++j) { if(!matchedCols[j]) { if (scores[i][j] > max) { max = scores[i][j]; maxCol = j; } } } return maxCol; } private int findRowMax(int j) { int maxRow = -1; double max = MIN_SCORE; for(int i=0;i<rows;++i) { if(scores[i][j] > max) { max = scores[i][j]; maxRow = i; } } return maxRow; } }