【问题标题】:Python intersection with custom equality具有自定义相等性的 Python 交集
【发布时间】:2016-10-17 04:55:40
【问题描述】:

我想构建两个 python 集的交集,但我需要有一个自定义相等性才能做到这一点。 有没有办法在做交叉点时直接指定“equaliy”?例如由于 lambda 表达式?

我知道有办法覆盖 eq,但我必须在同一个类上用不同的“等式”做几个交集。

谢谢!

【问题讨论】:

  • 你试过什么?您能否向我们展示一组最低限度的工作代码来演示您的问题以及您要完成的工作?
  • 您能否提供一个示例来说明您正在尝试做什么?这听起来有点违反使用集合的直觉,因为集合中唯一的部分对象是具有唯一的__hash__
  • 即使在数学上这也没有意义。集合被定义为没有重复项。本质上,一个集合中的所有元素都不相等,因此要在一个集合中,所有元素之间的相等比较必须已经存在。如果你能解释你想要完成的更多事情,它可能会让你在这些交叉路口的动机更加清晰!

标签: python lambda intersection


【解决方案1】:

前言

通过使用正确的术语,您正在尝试做的事情具有完美的数学意义。您所指的“平等”实际上是equivalence relations。基本上,等价关系描述了一个必须满足的条件,以便将集合中的两个元素标识为“等价”。

等价关系不相交地将一个集合分解为所谓的等价类。每个等价类都是一个子集,其中包含原始集合中关于等价关系成对等价的所有元素。

等价本身是等价关系的一个特例。等价关系的每个等价类只包含一个元素,因为集合中的每个元素都只等于它自己。

Excursion:当谈到面向对象语言中的对象相等性时,这是一个混乱的根源。在数学中,一个对象(即一个集合的成员)只存在一次(它只等于它自己)。然而,在面向对象的编程中,存在身份和平等的区别。可以有不同身份的对象(Python:a is b 计算结果为 false),它们相等(a == b 计算结果为 true),例如 float 2.0int 2。这意味着,Python 的 __eq__ 函数在所有 Python 对象的集合上实现了默认的等价关系,称为“平等”。 游览结束

关于任何等价类的一个更重要的事情是,它可以由单个任意成员(称为“代表”)表示。这意味着您只需检查与等价类的一个已知代表的关系,以确定元素是否属于该等价类。这将在下面的代码中被利用。

问题的答案

针对您的问题,我们有以下设置。我们有两个集合AB,它们实际上是更大集合M 的子集。对于M,有许多不同的等价关系,对于每一种,我们需要以某种方式“相交”AB“关于等价关系”。

唯一有意义的方法是将AB 的成员替换为它们的等价类,并检查两个投影中确实出现了哪些等价类。

这比听起来容易:关于一个等价关系的交集配方:

  1. A(和B)的元素进行分组,使每个组由成对的等效元素组成。 (这些组类似于等价类)
  2. 对于A 的每个组G,检查是否存在B 的组H,使得GH 的元素是等价的。如果是,GH 属于同一个等价类,即该等价类是交集的成员。

请注意,所需的输出实际上取决于您的用例。你可以例如使用匹配Gs 和Hs 的所有联合的列表(这是下面实施的)。或者,如果您只对交集中每个等价类的任意元素感兴趣,您可以选择G(或H)的第一个成员。 (这在__main__ 部分中显示为[c[0] for c in intersection]。)

下面的代码示例使用列表(不是集合或代表)实现了如上所述的简单交集,该列表适用于任何对象和任何等价关系。 Intersector 类接受一个带有两个参数的函数,如果两个参数相等则返回 true。

我假设您可以轻松优化您的用例以保存循环和比较。

重要提示:当然,您需要验证您的“等式”是实际等价关系(自反性、对称性、传递性,请参阅上面的 wiki 链接)。

代码:

from __future__ import print_function

class Intersector(object):
    def __init__(self, relation):
        self.relation = relation

    def intersect(self, a, b):
        a_classes = self.get_equivalence_classes(a)
        print('Groups of A:', a_classes)
        b_classes = self.get_equivalence_classes(b)
        print('Groups of B:', b_classes)
        return self.intersect_classes(a_classes, b_classes)

    def get_equivalence_classes(self, elements):
        eq_classes = []
        for element in elements:
            match = False
            for eq_class in eq_classes:
                if self.relation(element, eq_class[0]):
                    eq_class.append(element)
                    match = True
                    break

            if not match:
                eq_classes.append([element])
        return eq_classes

    def intersect_classes(self, a, b):
        def search_in_B(g):
            for h in b:
                if self.relation(g[0], h[0]):
                    return h
        result = []
        for g in a:
            h = search_in_B(g)
            if h is not None:
                result.append(g+h)
        print('Intersection:', result)
        return result


if __name__ == '__main__':
    A = set([4, 2, 1, 7, 0])
    B = set([1, 13, 23, 12, 62, 101, 991, 1011, 1337, 66, 666])

    print('A:', A)
    print('B:', B)
    print(79*'-')

    print('Intersection with respect to the relation:')
    print('a and b have the same remainder divided by 5')
    intersection = Intersector(lambda x, y : x % 5 == y % 5).intersect(A, B)
    reduced = [c[0] for c in intersection]
    print('Intersection reduced to representatives:', reduced)

    print(79*'-')

    print('Intersection with respect to the relation:')
    print('a and b have the same remainder divided by 7')
    intersection = Intersector(lambda x, y : x % 7 == y % 7).intersect(A, B)
    reduced = [c[0] for c in intersection]
    print('Intersection reduced to representatives:', reduced)

输出:

A: set([0, 1, 2, 4, 7])
B: set([1, 66, 101, 12, 13, 1011, 23, 1337, 666, 62, 991])
-------------------------------------------------------------------------------
Intersection with respect to the relation:
a and b have the same remainder divided by 5
Groups of A: [[0], [1], [2, 7], [4]]
Groups of B: [[1, 66, 101, 1011, 666, 991], [12, 1337, 62], [13, 23]]
Intersection: [[1, 1, 66, 101, 1011, 666, 991], [2, 7, 12, 1337, 62]]
Intersection reduced to representatives: [1, 2]
-------------------------------------------------------------------------------
Intersection with respect to the relation:
a and b have the same remainder divided by 7
Groups of A: [[0, 7], [1], [2], [4]]
Groups of B: [[1, 666], [66, 101, 1011], [12], [13, 62], [23], [1337], [991]]
Intersection: [[0, 7, 1337], [1, 1, 666], [2, 23], [4, 991]]
Intersection reduced to representatives: [0, 1, 2, 4]

【讨论】:

  • 感谢您提供如此详细且非常有用的答案!
【解决方案2】:

一种方法是编写您自己的Set 子类并以这种方式实现自定义比较。然后,您只需为每个“平等”创建一个自定义“键集”的新实例,以便使用它来获得元素的交集。

例如:http://code.activestate.com/recipes/576932-sets-with-a-custom-equalityuniqueness-function/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 2010-09-27
    • 1970-01-01
    • 2021-05-22
    • 2013-01-30
    • 2019-10-03
    • 2018-07-30
    • 2011-11-28
    相关资源
    最近更新 更多