【发布时间】:2021-11-03 14:28:28
【问题描述】:
给定一个包含一些键值对的数组:
[
{'a': 1, 'b': 1},
{'a': 2, 'b': 1},
{'a': 2, 'b': 2},
{'a': 1, 'b': 1, 'c': 1},
{'a': 1, 'b': 1, 'c': 2},
{'a': 2, 'b': 1, 'c': 1},
{'a': 2, 'b': 1, 'c': 2}
]
我想找到这些对的交集。 交集 意味着只留下那些可以被其他人覆盖或独特的元素。例如,
{'a': 1, 'b': 1, 'c': 1} 和 {'a': 1, 'b': 1, 'c': 2} 完全覆盖 {'a': 1, 'b': 1},而 {'a': 2, 'b': 2} 是独一无二的。所以,在
[
{'a': 1, 'b': 1},
{'a': 2, 'b': 1},
{'a': 2, 'b': 2},
{'a': 1, 'b': 1, 'c': 1},
{'a': 1, 'b': 1, 'c': 2},
{'a': 2, 'b': 1, 'c': 1},
{'a': 2, 'b': 1, 'c': 2}
]
找到交叉点后应该保留
[
{'a': 2, 'b': 2},
{'a': 1, 'b': 1, 'c': 1},
{'a': 1, 'b': 1, 'c': 2},
{'a': 2, 'b': 1, 'c': 1},
{'a': 2, 'b': 1, 'c': 2}
]
我尝试遍历所有对并找到相互比较的覆盖对,但时间复杂度等于O(n^2)。 是否有可能在线性时间内找到所有覆盖或唯一对?
这是我的代码示例 (O(n^2)):
public Set<Map<String, Integer>> find(Set<Map<String, Integer>> allPairs) {
var results = new HashSet<Map<String, Integer>>();
for (Map<String, Integer> stringToValue: allPairs) {
results.add(stringToValue);
var mapsToAdd = new HashSet<Map<String, Integer>>();
var mapsToDelete = new HashSet<Map<String, Integer>>();
for (Map<String, Integer> result : results) {
var comparison = new MapComparison(stringToValue, result);
if (comparison.isIntersected()) {
mapsToAdd.add(comparison.max());
mapsToDelete.add(comparison.min());
}
}
results.removeAll(mapsToDelete);
results.addAll(mapsToAdd);
}
return results;
}
MapComparison 在哪里:
public class MapComparison {
private final Map<String, Integer> left;
private final Map<String, Integer> right;
private final ComparisonDecision decision;
public MapComparison(Map<String, Integer> left, Map<String, Integer> right) {
this.left = left;
this.right = right;
this.decision = makeDecision();
}
private ComparisonDecision makeDecision() {
var inLeftOnly = new HashSet<>(left.entrySet());
var inRightOnly = new HashSet<>(right.entrySet());
inLeftOnly.removeAll(right.entrySet());
inRightOnly.removeAll(left.entrySet());
if (inLeftOnly.isEmpty() && inRightOnly.isEmpty()) {
return EQUALS;
} else if (inLeftOnly.isEmpty()) {
return RIGHT_GREATER;
} else if (inRightOnly.isEmpty()) {
return LEFT_GREATER;
} else {
return NOT_COMPARABLE;
}
}
public boolean isIntersected() {
return Set.of(LEFT_GREATER, RIGHT_GREATER).contains(decision);
}
public boolean isEquals() {
return Objects.equals(EQUALS, decision);
}
public Map<String, Integer> max() {
if (!isIntersected()) {
throw new IllegalStateException();
}
return LEFT_GREATER.equals(decision) ? left : right;
}
public Map<String, Integer> min() {
if (!isIntersected()) {
throw new IllegalStateException();
}
return LEFT_GREATER.equals(decision) ? right : left;
}
public enum ComparisonDecision {
EQUALS,
LEFT_GREATER,
RIGHT_GREATER,
NOT_COMPARABLE,
;
}
}
【问题讨论】:
-
我不确定这是否可以在线性时间内完成,但如果您首先对数据进行排序,它可能在 O(n*log(n)) 中是可行的
-
相关关键字:在多目标优化领域,您尝试计算的子列表称为pareto front。
-
我想知道是否将每个元素视为多项式(假设每个键值对都可以唯一地散列)是否可以让人们找到多项式算术的交集。元素中的每一对都是第 n 阶系数。但是,需要更清楚地说明问题集 - 例如
{a:1, b:2}等于{b:2, a:1}-{a:1, c:1, d:1, b:1}是否包含{a:1, b:1}。我建议让您的输入集更加全面。 -
我觉得 union-find 实际上可能是这个问题的近似值。 (至少是算法的查找部分),即 O(log*(n))。可以从使用元素数量最少的集合开始,并将它们用作“查找”算法的元素。这将导致与@Thomas 答案相同的时间复杂度。我不认为一个人可以走得更快,尽管这可能会引起争论。赞成这个问题,因为算法总是很有趣。编辑:根据cstheory.stackexchange.com/a/41388/62830 不可能在 O(n) 中做到这一点
-
我不了解 java,但Fast calculation of Pareto front in Python 的公认答案在 4 秒内解决了 10,000 个数组和每个数组 15 个键值的问题。这对您来说足够高效吗?
标签: java algorithm time-complexity