[英]What is an efficient way to find the highest average similarity/distance between two collections?
假設我有集合A
和集合B
的大小不一定相同。
然后我想為A
中的每個a
和B
中的每個b
找到一組得分最高的對(a, b)
。
主要規定是A
中的每個a
和B
中的每個b
只能使用一次。 所以如果score(a1, b1) == score(a1, b2)
我們只能保留兩個分數之一。
這是一個具有虛構相似性矩陣的具體示例。 每行代表集合 A 的一個元素,每一列是集合 B 的一個元素。所以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}};
我們首先會說(0,1)
包含第 1 行中的最高分。因此a_0
和b_1
不再可用於任何匹配。
接下來,我們會說(1, 0)
或(1, 3)
包含第 2 行中的最高分。由於兩者都是公平游戲,我們選擇(1, 0)
。 現在, a_1
和b_0
是禁區。
最后,我們看到第三行的最高分在(2, 1)
處。 但是因為B
中的b_1
是針對的,所以我們必須選擇別的東西。 我們改為選擇(2, 3)
。
所以我們沒有重復的成對最高得分對是(a_0, b_1), (a_1, b_0), (a_2, b_3)
。
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();
}
但是,這會引發異常,因為我正在迭代並同時更改集合。 我已閱讀並理解該錯誤。 我想不出是一種有效的選擇。
也許走上使用相似矩陣的道路不是一個好主意? 歡迎任何建議或提示。
編輯我剛剛用 rowIdxs.iterator() 替換了 rowIdxs 並單步調試了我的調試器。 即使不拋出異常,上述邏輯也不起作用。
主要問題是,即使我在跟蹤使用過的元素/坐標,我仍在查詢它們。 在這里,我決定采用不同的方法來實現這一目標:
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());
List<List<Integer>> coords = new ArrayList<>(Sets.cartesianProduct(rowIdxs, colIdxs));
List<Integer> setA = new ArrayList<>();
List<Integer> setB = new ArrayList<>();
Map<List<Integer>, Double> scores = new HashMap<>();
coords.forEach(c -> scores.put(c, simMatrix.getEntry(c.get(0), c.get(1))));
coords.sort(Comparator.comparing(scores::get).reversed());
double score = 0;
int requiredMet = 0;
int required = Math.min(rowIdxs.size(), colIdxs.size());
for (List<Integer> coord : coords) {
int x = coord.get(0);
int y = coord.get(1);
if (!setA.contains(x) && !setB.contains(y)) {
setA.add(x);
setB.add(y);
score += scores.get(coord);
requiredMet += 1;
}
if (requiredMet == required) {
break;
}
}
return required == 0 ? 0 : score / required;
}
聽起來您在描述經典的作業問題。
問題實例有許多代理和許多任務。 可以分配任何代理來執行任何任務,產生的成本可能會因代理任務分配而異。 要求執行盡可能多的任務,每個任務最多分配一個代理,每個代理最多分配一個任務,以使分配的總成本最小化。
您有一堆要分配給不同任務(列)的代理(行),兩者之間的關系是一對一的。 你想最小化成本(最大化你的利潤/分數)。
解決此問題的一種選擇是使用Hungarian Algorithm 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.