如何识别一对中的两个元素都大于集合中其他元素的情况?

问题描述 投票:0回答:1

我有一个情况,我有一个对列表,每个对都有两个数值。我想找到这些元素的子集,仅包含那些不被另一个元素的两个元素超过的对(比方说被另一个元素“黯然失色”)。 例如,对 (1,2) 被 (4,5) 掩盖,因为两个元素都小于另一对中的相应元素。

此外,(1,2) 被认为比 (1,3) 黯然失色,因为第一个元素等于另一个元素,而第二个元素小于另一个元素。

然而,(2, 10) 不会被 (9, 9) 掩盖,因为只有其中一个元素被另一个元素超过。

配对相同的情况应减少到只有一个(删除重复项)。

最终,我希望将配对列表减少到一个子集,其中仅保留未被任何其他配对所掩盖的配对。

例如,采用以下列表:

(1,2) (1,5) (2,2) (1,2) (2,2) (9,1) (1,1)

这应该减少到以下内容:

(1,5) (2,2) (9,1)

我在 python 中的最初实现如下,使用极坐标:

import polars as pl pairs_list = [ (1,2), (1,5), (2,2), (1,2), (2,2), (9,1), (1,1), ] # tabulate pair elements as 'a' and 'b' pairs = pl.DataFrame( data=pairs_list, schema={'a': pl.UInt32, 'b': pl.UInt32}, orient='row', ) # eliminate any duplicate pairs unique_pairs = pairs.unique() # self join so every pair can be compared (except against itself) comparison_inputs = ( unique_pairs .join( unique_pairs, how='cross', suffix='_comp', ) .filter( pl.any_horizontal( pl.col('a') != pl.col('a_comp'), pl.col('b') != pl.col('b_comp'), ) ) ) # flag pairs that were eclipsed by others comparison_results = ( comparison_inputs .with_columns( pl.all_horizontal( pl.col('a') <= pl.col('a_comp'), pl.col('b') <= pl.col('b_comp'), ) .alias('is_eclipsed') ) ) # remove pairs that were eclipsed by at least one other principal_pairs = ( comparison_results .group_by('a', 'b') .agg(pl.col('is_eclipsed').any()) .filter(is_eclipsed=False) .select('a', 'b') )

虽然这看起来确实有效,但由于自连接表的庞大规模,对于大型数据集来说,在计算上是不可行的。

我考虑过通过删除冗余的反向比较来过滤比较输入表,例如,X对与Y对以及Y对与X对不需要都像当前一样位于表中,但更改需要附加条件在每次比较中报告哪个元素在比较中黯然失色,并且仅将数据集减少一半,这并不那么重要。

我发现在执行自连接步骤之前,我可以通过执行窗口函数过滤器来大大减少所需的比较,该窗口函数过滤器仅过滤每个 a 的最大值 b,反之亦然。换句话说:

unique_pairs = ( pairs .unique() .filter(a = pl.col('a').last().over('b', order_by='a') .filter(b = pl.col('b').last().over('a', order_by='b')

但是当然这只能做这么多并且取决于 a 和 b 的基数。之后我还需要自加入并比较才能得到结果。

我很好奇是否已经建立了一些算法来计算这个值,以及是否有人有更有效的方法的想法。无论如何,有兴趣了解更多。预先感谢。

python join optimization mathematical-optimization self-join
1个回答
0
投票
des

顺序排列,第一个元素中存在联系,然后按

des
顺序
 对第二个元素进行排序
unique_pairs = sorted(set(pairs), key=lambda x: (-x[0], -x[1]))

通过保持每对的条件 If - b 大于迄今为止所有具有较大第一个元素的对的最大第二个元素,该对不能被遮盖。

from typing import List, Tuple import bisect def find_non_eclipsed_pairs(pairs: List[Tuple[int, int]]) -> List[Tuple[int, int]]: if not pairs: return [] unique_pairs = sorted(set(pairs), key=lambda x: (-x[0], -x[1])) result = [] max_second_elements = [] for pair in unique_pairs: if not max_second_elements or pair[1] > max_second_elements[-1]: result.append(pair) while max_second_elements and max_second_elements[-1] <= pair[1]: max_second_elements.pop() max_second_elements.append(pair[1]) return sorted(result)

测试

def test_pareto_pairs(): test_cases = [ ( [(1,2), (1,5), (2,2), (1,2), (2,2), (9,1), (1,1)], [(1,5), (2,2), (9,1)] ), ( [], [] ), ( [(1,1)], [(1,1)] ), ( [(1,1), (2,2), (3,3), (4,4)], [(4,4)] ), ( [(1,5), (5,1)], [(1,5), (5,1)] ), ( [(1,1), (1,2), (2,1), (2,2), (3,1), (1,3)], [(1,3), (2,2), (3,1)] ) ] for i, (input_pairs, expected) in enumerate(test_cases, 1): result = find_non_eclipsed_pairs(input_pairs) assert result == sorted(expected), f"Test case {i} failed: expected {expected}, got {result}" print(f"Test case {i} passed") if __name__ == "__main__": test_pareto_pairs() pairs_list = [ (1,2), (1,5), (2,2), (1,2), (2,2), (9,1), (1,1), ] result = find_non_eclipsed_pairs(pairs_list) print("\nOriginal pairs:", pairs_list) print("Non-eclipsed pairs:", result)

哪些结果

=================== RESTART: C:/Users/Bhargav/Desktop/test.py ================== Test case 1 passed Test case 2 passed Test case 3 passed Test case 4 passed Test case 5 passed Test case 6 passed Original pairs: [(1, 2), (1, 5), (2, 2), (1, 2), (2, 2), (9, 1), (1, 1)] Non-eclipsed pairs: [(1, 5), (2, 2), (9, 1)]

时间复杂度 - O(n log n)
空间复杂度为 O(n)

© www.soinside.com 2019 - 2024. All rights reserved.