在 Python 中的某些约束下生成集合的最大子集

如何解决在 Python 中的某些约束下生成集合的最大子集

我有一组属性 A= {a1,a2,...an} 和一组集群 C = {c1,c2,... ck}我有一组对应 COR,它是 A x C 和 {{1 }}。这是一组示例对应关系

|COR|<< A x C

现在,我想生成 COR = {(a1,c1),(a1,c2),(a2,(a3,c3),(a4,c4)} 的所有子集,使得子集中的每一对代表从集合 COR 到集合 A一个单射函数。让我们将每个这样的子集称为映射,那么来自上述集合 C 的有效映射将是
CORm1 = {(a1,c4)}
m2 = {(a1,c4)} 在这里很有趣,因为添加m1COR 的任何剩余元素要么违反函数的定义,要么违反成为单射函数的条件。例如,如果我们将 m1添加(a1,c2)m1 将不再是一个函数,如果我们将 m1 添加(a2,c1),它将停止成为一个单射函数。因此,我对一些可用于生成所有此类映射的代码片段或算法感兴趣。这是我迄今为止在 python 中尝试过的 进口藏品 导入迭代工具

m1

正如预期的那样,代码生成 corr = set({('a1','c1'),('a1','c2'),('a2',('a3','c3'),('a4','c4')}) clusters = [c[1] for c in corr] attribs = [a[0] for a in corr] rep_clusters = [item for item,count in collections.Counter(clusters).items() if count>1] rep_attribs = [item for item,count in collections.Counter(attribs).items() if count>1] conflicting_sets = [] for c in rep_clusters: conflicting_sets.append([p for p in corr if p[1] == c]) for a in rep_attribs: conflicting_sets.append([p for p in corr if p[0] == a]) non_conflicting = corr for s in conflicting_sets: non_conflicting = non_conflicting - set(s) m = set() for p in itertools.product(*conflicting_sets): print(p,'product',len(p)) p_attribs = set([k[0] for k in p]) p_clusters = set([k[1] for k in p]) print(len(p_attribs),len(p_clusters)) if len(p) == len(p_attribs) and len(p) == len(p_clusters): m.add(frozenset(set(p).union(non_conflicting))) print(m) 不生成 m2,因为 m1 不会从 m1 生成。任何人都可以指导我吗?我还需要一些性能方面的指导,因为实际集合会比此处使用的 itertools.product 集合大,并且可能包含更多冲突集合。

解决方法

您的需求的更简单定义是:

  • 您有一组独特的元组。
  • 您想生成所有子集,其中:
    • 元组的所有第一个元素都是唯一的(以确保功能);
    • 并且所有第二个元素都是唯一的(以确保注入性)。
  • 您的标题表明您只想要最大子集,即在不违反其他要求的情况下,必须不可能从原始集合中添加任何其他元素。

我还假设任何 a<x>c<y> 都是唯一的。

这是一个解决方案:

def get_maximal_subsets(corr):
    def is_injective_function(f):
        if not f:
            return False
        f_domain,f_range = zip(*f)
        return len(set(f_domain)) - len(f_domain) + len(set(f_range)) - len(f_range) == 0

    def generate_from(f):
        if is_injective_function(f):
            for r in corr - f:
                if is_injective_function(f | {r}):
                    break
            else:
                yield f
        else:
            for c in f:
                yield from generate_from(f - {c})

    return list(map(set,set(map(frozenset,generate_from(corr)))))


# representing a's and c's as strings,as their actual value doesn't matter,as long as they are unique
print(get_maximal_subsets(corr={('a1','c1'),('a1','c2'),('a2',('a3','c3'),('a4','c4')}))

测试 is_injective_function 检查提供的集合 f 是否表示有效的单射函数,方法是从函数的域和范围中获取所有值,并检查两者是否仅包含唯一值。>

生成器接受一个 f,如果它代表一个单射有效函数,它会检查是否没有从原始 corr 中删除以到达 f可以重新添加,同时仍然让它代表一个单射有效函数。如果是这种情况,它会产生 f 作为有效结果。

如果 f 不是一个单射有效函数,它将尝试依次删除 f 中的每个元素,并从每个子集中生成任何单射有效函数。

最后,整个函数从生成的生成器中删除重复项,并将其作为唯一集列表返回。

输出:

[{('a1','c4')},{('a2','c4'),'c2')}]

注意,有多种方法可以对不可散列值的列表进行重复数据删除,但是这种方法会将列表中的所有集合转换为 frozenset 以使其可散列,然后将列表转换为集合以删除重复项,然后再次将内容转成集合,并以列表形式返回结果。

您可以通过跟踪已尝试删除的子集来防止在最后删除重复项,这可能会根据您的实际数据集表现得更好:

def get_maximal_subsets(corr):
    def is_injective_function(f):
        if not f:
            return False
        f_domain,f_range = zip(*f)
        return len(set(f_domain)) - len(f_domain) + len(set(f_range)) - len(f_range) == 0

    previously_removed = []

    def generate_from(f,removed: set = None):
        previously_removed.append(removed)
        if removed is None:
            removed = set()
        if is_injective_function(f):
            for r in removed:
                if is_injective_function(f | {r}):
                    break
            else:
                yield f
        else:
            for c in f:
                if removed | {c} not in previously_removed:
                    yield from generate_from(f - {c},removed | {c})

    return list(generate_from(corr))

这可能是一个总体上性能更好的解决方案,但我更喜欢第一个的简洁算法进行解释。

在评论询问它是否可以扩展到 100 个元素并包含约 15 个冲突(它会运行很多分钟来解决它)之后,我对上述解决方案的缓慢感到恼火,所以这里有一个运行速度低于 1 秒的更快的解决方案对于有 15 个冲突的 100 个元素,虽然执行时间仍然呈指数级增长,因此它有其限制):

def injective_function_conflicts(f):
    if not f:
        return {}
    conflicts = defaultdict(set)
    # loop over the product f x f
    for x in f:
        for y in f:
            # for each x and y that have a conflict in any position
            if x != y and any(a == b for a,b in zip(x,y)):
                # add x to y's entry and y to x's entry
                conflicts[y].add(x)
                conflicts[x].add(y)
    return conflicts


def get_maximal_partial_subsets(conflicts,off_limits: set = None):
    if off_limits is None:
        off_limits = set()

    while True and conflicts:
        # pop elements from the conflicts,using them now,or discarding them if off-limits
        k,vs = conflicts.popitem()
        if k not in off_limits:
            break
    else:
        # nothing left in conflicts that's not off-limits
        yield set()
        return

    # generate each possible result from the rest of the conflicts,adding the conflicts vs for k to off_limits
    for sub_result in get_maximal_partial_subsets(dict(conflicts),off_limits | vs):
        # these results can have k added to them,as all the conflicts with k were off-limits
        yield sub_result | {k}
    # also generated each possible result from the rest of the conflicts without k's conflicts
    for sub_result in get_maximal_partial_subsets(conflicts,off_limits):
        # but only yield as a result if adding k itself to it would actually cause a conflict,avoiding duplicates
        if sub_result and injective_function_conflicts(sub_result | {k}):
            yield sub_result


def efficient_get_maximal_subsets(corr):
    conflicts = injective_function_conflicts(corr)
    final_result = list((corr - set(conflicts.keys())) | result
                        for result in get_maximal_partial_subsets(dict(conflicts)))
    print(f'size of result and conflict: {len(final_result)},{len(conflicts)}')
    return final_result


print(efficient_get_maximal_subsets(corr={('a1','c4')}))

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?