【问题标题】:merge lists with intersection将列表与交集合并
【发布时间】:2015-01-06 05:34:24
【问题描述】:

鉴于:

g=[[], [], [0, 2], [1, 5], [0, 2, 3, 7], [4, 6], [1, 4, 5, 6], [], [], [3, 7]]

如何比较 g 中的每个列表,以便共享任何公共编号的列表可以合并到一个集合中?

例如
0 存在于 g[2]g[4] 所以他们合并到一个集合{0,2,3,7}

我尝试了以下方法,但它不起作用:

for i in g:
    for j in g:
        if k in i == l in j:
            m=set(i+j)

我想制作尽可能大的集合。

【问题讨论】:

  • 欢迎来到 scicomp。由于您的问题是特定于 python 的,我建议将其迁移到“stackoverflow”页面...
  • 你能给出预期的输出吗?

标签: python algorithm set


【解决方案1】:

作为一种快得多的方式,您可以首先创建一个 len 大于一个 (s) 的项目集列表。然后浏览您的列表并使用union 函数进行更新!

s=map(set,g)
def find_intersection(m_list):
    for i,v in enumerate(m_list) : 
        for j,k in enumerate(m_list[i+1:],i+1):
           if v & k:
              m_list[i]=v.union(m_list.pop(j))
              return find_intersection(m_list)
    return m_list

演示:

g=[[], [], [0, 2], [1, 5], [0, 2, 3, 7], [4, 6], [1, 4, 5, 6], [], [], [3, 7]]
s=map(set,g)
print find_intersection(s)

[set([0, 2, 3, 7]), set([1, 4, 5, 6])]

g=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
s=map(set,g)
print find_intersection(s)

[set([1, 2, 3, 4, 5, 6, 7]), set([9, 10, 11])]

g=[[], [1], [0,2], [1, 5], [0, 2, 3, 7], [4, 6], [1, 4, 5, 6], [], [], [3, 7]]
s=map(set,g)
print find_intersection(s)

[set([1, 4, 5, 6]), set([0, 2, 3, 7])]

以@Mark 的回答作为基准:

from timeit import timeit


s1="""g=[[], [], [0, 2], [1, 5], [0, 2, 3, 7], [4, 6], [1, 4, 5, 6], [], [], [3, 7]]
sets = [set(i+j) for i in g for j in g if i!=j and (set(i) & set(j))]
    """
s2="""g=[[], [], [0, 2], [1, 5], [0, 2, 3, 7], [4, 6], [1, 4, 5, 6], [], [], [3, 7]]

s=map(set,g)

def find_intersection(m_list):
    for i,v in enumerate(m_list) : 
        for j,k in enumerate(m_list[i+1:],i+1):
           if v & k:
              s[i]=v.union(m_list.pop(j))
              return find_intersection(m_list)
    return m_list 
    """

print ' first: ' ,timeit(stmt=s1, number=100000)
print 'second : ',timeit(stmt=s2, number=100000)

first:  3.8284008503
second :  0.213887929916

【讨论】:

  • 您的算法是正确的,但它的实现存在缺陷:尝试使用g=[[], [1], [0,2], [1, 5], [0, 2, 3, 7], [4, 6], [1, 4, 5, 6], [], [], [3, 7]] 问题是使用s[i:] 创建了s 切片的副本,而没有得到更新s.pop(t)
  • @PabloFranciscoPérezHidalgo 感谢您的注意,已修复。
【解决方案2】:

这里有一个快速的方法,它将列出所有相交的集合:

sets = [set(i+j) for i in g for j in g if i!=j and (set(i) & set(j))]

请注意,每个结果都会重复,因为每个列表都会被比较两次,一次在左侧,一次在右侧。

【讨论】:

  • 在这种情况下,您可能想要这样做:set(frozenset(i+j) for i in g for j in g if i!=j and (set(i) & set(j)))。它将摆脱重复项(假设这是 OP 想要的)。
  • 对不起,请问什么是(set(i) & set(j)?
  • @NaLai ijg 中的条目。它们是列表。 set(i)set(j) 是这些列表的集合版本。 set(i) & set(j)set(i)set(j) 的交集。相应地,| 为交集,^ 为对称差。
  • @NaLai 见stackoverflow.com/questions/642763/…。如果集合的交集为空,if 将评估为 false,否则为 true。
  • @iCodez 如何提取 freezeset 中没有“frozenset”一词的集合?
【解决方案3】:

如果gg 的元素很大,您可以使用不相交集来提高效率。

此数据结构可用于将每个元素分类到它应属于的集合中。

第一步是构建一个不相交集集合,其中所有g 集都由它们在g 中的索引标记:

g=[[], [], [0, 2], [1, 5], [0, 2, 3, 7], [4, 6], [1, 4, 5, 6], [], [], [3, 7],[99]]
g = map(set, g)
dss = CDisjointSets()
for i in xrange(len(g)):
    dss.MakeSet(i)

然后,只要交集不为空,集合就会加入:

for i in xrange(len(g)):
    for j in xrange(i+1, len(g)):
        if g[i].intersection(g[j]):
            dss.Join(i,j)

此时,dss 为您提供了应连接在一起的 g 集合的通用标签:

print(dss)

父(0) = 0 父母(1)= 1 父母(2)= 2 父母(3)= 3 父母(4)= 2 父母(5)= 3 父母(6)= 3 父母(7)= 7 父母(8)= 8 父母(9)= 2 父(10) = 10

现在您只需构建新的集合,加入具有相同标签的集合:

l2set = dict()
for i in xrange(len(g)):
    label = dss.FindLabel(i).getLabel()
    l2set[label] = l2set.get(label, set()).union(g[i])
print(l2set)

导致:

{0: set([]), 1: set([]), 2: set([0, 2, 3, 7]), 3: set([1, 4, 5, 6]), 7: set([]), 8:   set([]), 10: set([99])}

这是我使用的不相交集的实现,但您肯定可以找到另一个具有更好sintax的实现:

""" Disjoint Sets
    -------------
    Pablo Francisco Pérez Hidalgo
    December,2012. """
class CDisjointSets:

    #Class to represent each set
    class DSet:
        def __init__(self, label_value):
            self.__label = label_value
            self.rank = 1
            self.parent = self
        def getLabel(self):
            return self.__label

    #CDisjointSets Private attributes
    __sets = None

    #CDisjointSets Constructors and public methods.
    def __init__(self):
        self.__sets = {}

    def MakeSet(self, label):
        if label in self.__sets: #This check slows the operation a lot,
            return False         #it should be removed if it is sure that
                                 #two sets with the same label are not goind
                                 #to be created.
        self.__sets[label] = self.DSet(label)

    #Pre: 'labelA' and 'labelB' are labels or existing disjoint sets.
    def Join(self, labelA, labelB):
        a = self.__sets[labelA]
        b = self.__sets[labelB]
        pa = self.Find(a)
        pb = self.Find(b)
        if pa == pb: 
            return #They are already joined
        parent = pa
        child = pb
        if pa.rank < pb.rank:
            parent = pb
            child = pa
        child.parent = parent
        parent.rank = max(parent.rank, child.rank+1)

    def Find(self,x):
        if x == x.parent:
            return x
        x.parent = self.Find(x.parent)
        return x.parent

    def FindLabel(self, label):
        return self.Find(self.__sets[label])

    def __str__(self):
        ret = ""
        for e in self.__sets:
            ret = ret + "parent("+self.__sets[e].getLabel().__str__()+") = "+self.FindLabel(e).parent.getLabel().__str__() + "\n"
        return ret

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    相关资源
    最近更新 更多