The problem:
Let's say I have collection A and collection B not necessarily of equal size.
Then I want to find the set of highest scoring pairs (a, b) for each a in A and each b in B.
The main stipulation is that each a in A and each b in B can only be used once. So if score(a1, b1) == score(a1, b2) we can only keep one of the two scores.
Here's a concrete example with a made-up similarity matrix. Each row represents an element from collection A and each column is an element of collection B. so M[i][j] = score(a_i, b_j)
new double[][]{{1, 4, 1, 1}, // 4 occurs twice in a column
{3, 1, 2, 3}, // 3 occurs twice in a row
{1, 4, 1, 1}};
We would first say that (0,1) contains the highest score in row 1. So a_0 and b_1 is no longer available for any match-ups.
Next, we would say that (1, 0) or (1, 3) contain the highest score in row 2. Since either is fair game we choose (1, 0). Now, a_1 and b_0 are off-limits.
Finally, we see the highest score in row three is at (2, 1). But because b_1 in B is spoken for, we have to choose something else. We instead choose (2, 3).
So our pairwise highest scoring pairs without repeitition are (a_0, b_1), (a_1, b_0), (a_2, b_3).
Here's what I've tried:
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealVector;
import org.apache.commons.math3.util.Pair;
public static double rankBySimilarity(Array2DRowRealMatrix simMatrix) {
Set<Integer> rowIdxs =
IntStream.range(0, simMatrix.getRowDimension()).boxed().collect(Collectors.toSet());
Set<Integer> colIdxs =
IntStream.range(0, simMatrix.getColumnDimension()).boxed().collect(Collectors.toSet());
Set<Pair<Integer, Integer>> bestScoreIdxs = new HashSet<>();
for (int row : rowIdxs) {
RealVector rowVec = simMatrix.getRowVector(row);
int col = rowVec.getMaxIndex();
bestScoreIdxs.add(new Pair<>(row, col));
rowIdxs.remove(row);
colIdxs.remove(col);
if (rowIdxs.isEmpty() || colIdxs.isEmpty()) {
break;
}
}
double score = 0;
for (Pair<Integer, Integer> coord : bestScoreIdxs) {
int x = coord.getFirst();
int y = coord.getSecond();
score += simMatrix.getEntry(x, y);
}
return score / bestScoreIdxs.size();
}
However, this throws an exception because I'm iterating over and altering a collection at the same time. I have read up and understood the error. What I can't figure out is an efficient alternative.
Maybe going down the path of using a similarity matrix isn't a good idea? Any suggestions or hints are welcome.
Edit I just replaced rowIdxs with rowIdxs.iterator() and stepped through my debugger. The above logic doesn't work even if it doesn't throw an exception.