【问题标题】:Creating a progressive list iteration in Python在 Python 中创建渐进式列表迭代
【发布时间】:2014-09-05 04:27:10
【问题描述】:

我有一个包含线条(几何)的列表。这些线条组成了多种形状(正方形、矩形等)。但是,它们被分解了,我正在尝试一种方法将它们按它们的形状组合成单独的列表。例如,如果我的列表包含 8 行,并且我知道它们有两个矩形形状,我想创建两组,每组四行。 现在,我可以查询每一行的 StartPoint() 和 EndPoint()。我正在考虑设置一个循环遍历列表的循环。选择列表中的第一行。获取它的终点,然后检查它是否等于列表中其余行的任何起点。如果是,则将该行与第一行一起添加到列表中。然后取第二行并做同样的事情,直到行的起点等于最后一行的终点,第一行的起点等于它的终点。 我该怎么做?

lines = [line1, line2, line3, line4, line5, line6, line7] #that means i have a square and a triangle
for i in lines:
    shape1.append(lines[0])
    sPoint = lines[0].StartPoint()
    ePoint = lines[0].EndPoint()
    if i.StartPoint() == ePoint:
        shape1.append(i)

我不知道如何自动创建“形状”列表以及如何打破循环,这样我就不会绕圈子了。

任何帮助将不胜感激。 谢谢,

【问题讨论】:

    标签: python loops iteration


    【解决方案1】:

    如果你花一些时间来抽象你的问题,你会发现你真的只是在做图表。

    图由顶点和边组成。顶点是您的起点和终点。

    我写了一段代码,一旦您了解如何将数据转换为建议的格式,它应该可以帮助您解决问题。

    注释:

    我建议您在阅读我的代码时阅读内置类型 set()

    vertices = set(['A', 'C', 'B', 'E', 'D', 'G', 'F'])
    
    #think of this as Line1.StartPoint()='A' Line1.EndPoint()='B' ... 
    edges = {'A': 'B',
             'B': 'C',
             'C': 'D',
             'D': 'A',
             'E': 'F',
             'F': 'G',
             'G': 'E'}
    
    shapes = set()
    
    #Check if we tested all edges
    while len(vertices) is not 0:
        next = vertices.pop()
        shape = set()
        while next not in shape:
            shape.add(next)
            next = edges[next]
        shapes.add(frozenset(shape))
    
    print shapes
    

    结果:

    >>set([frozenset(['A', 'C', 'B', 'D']), frozenset(['E', 'G', 'F'])])

    编辑:主要的加速是可能的,但为了简单起见省略了。

    【讨论】:

    • Christoph,这需要如何重组才能使用元组?不幸的是,我正在使用的 python 编译器目前不支持字典(我知道它很陈旧)但是如果我可以让它与一个很棒的元组列表一起工作。
    • @konrad 你需要更具体一点;)你的数据现在是什么样子的?你有顶点和边的集合吗?
    • 是的,我有一个边列表,我可以有一个匹配的端点/起点列表。不幸的是,我无法从它们创建字典,因为我正在使用的编译器中有一个错误。 :-(
    • 此外,在@konrad 的情况下,该图事先并不存在(与您的代码中您假设边中已经存储了一个图不同)。有一个简单的行列表。由程序员来构建图表,因为问题似乎已经给出。
    【解决方案2】:

    不是最好的算法,但我困了,这应该让你开始:

    lines = [line1, line2, line3, line4, line5, line6, line7]
    shapes = []
    
    while lines:
        shape = [lines[0]]
        i=1
        while i < len(lines):
            line1 = lines[i]
            for line2 in lines[i+1:]:
                if line2.start == line1.end or line2.end == line1.start or \
                   line2.start == line1.start or line2.end == line1.end:
    
                    shape.append(line2)
                    lines.pop(i)
                    i -= 1
                    if line2.end == shape[0].start or line2.start == shape[0].end or \
                       line2.end == shape[0].end or line2.start == shape[0].start:
    
                        shapes.append(shape)
                        shape = []
            i += 1
    

    为了改善这一点,尝试按起点和终点对线进行排序,并对连接线进行二分搜索

    【讨论】:

    • 我不确定这段代码有什么问题,但它陷入了永无止境的循环。
    • @konrad:抱歉,忘记增加i。现已修复
    • 我又试了一次,还是不行。它又卡住了,我不得不重新启动。 :-( 第一个 while 行的目的是什么?这不总是评估为真吗?即使您从该列表中弹出所有元素,它仍然不是一个列表吗?对于所有这些菜鸟问题,我很抱歉。我正在尝试理解这个算法。
    • 现在我知道它有什么问题了。这是因为不能保证一条线的终点就是另一条线的起点。我的意思是如果他们有相反的方向,那么他们都可以在同一个位置有“起点”。这苏克斯。任何想法如何处理?
    • @konrad:查看编辑。我在 if 语句中添加了额外的条件来处理这个问题(抱歉耽搁了,我刚下班回来)
    【解决方案3】:

    预赛

    首先,我们定义一个line类,其中包含2个点的坐标:

    import itertools
    import random
    
    class line(object) :
        """
        A line has 2 ends.
        """
        def __init__(self, tup0, tup1 ) :
            self.tup0 = tup0
            self.tup1 = tup1
    
        def __repr__( self ) :
            return "line[{0}, {1}]".format( self.tup0, self.tup1 )
    
        def StartPoint(self) :
            return self.tup0
    
        def EndPoint(self):
            return self.tup1
    
        def reverseLine(self) :
            return line( self.tup1, self.tup0 )
    

    解决方案

    我只是枚举所有可能的形状(有效或无效),直到找到 2 个闭合形状:

    • 我列举了行列表中所有可能的 2 路分区
    • 给定分区的每个集群都是一个可能的形状。为了实际验证这一点,我列举了集群中所有可能的线顺序排列(以及每条线的终点的所有可能顺序)
    • 一旦我找到了一个可以验证我们标准的分区(即每个集群都是一个封闭的形状),我打印解决方案并停止

    这里是帮手:

    def is_closed( lines ) :
        """
        Return True if lines represents a closed shape (i.e., order matters)
        """
        are0ToNConnected = all( l.tup1 == lines[i+1].tup0 for i,l in enumerate( lines[:-1] ) ) 
        areFirstAndLastConnected = ( lines[-1].tup1 == lines[0].tup0 )
        return are0ToNConnected and areFirstAndLastConnected
    
    def is_any_closed( lines ) :
        """
        Return True if at least one re-ordering of lines represents a closed shape (i.e., order doesnt matter)
        """
        return any( is_closed(newLines) 
                   for permutedLines in itertools.permutations( lines ) 
                   for newLines in itertools.product( * [ ( l, l.reverseLine() ) for l in permutedLines ] ) )
    
    def k_way_partition( A, k ) :
        """
        Generator for all k-way partitions of A
        """
        if k == 1 :
            yield [ A ]
        elif len(A) == k :
            yield [ [ a ] for a in A ]
        else :
            for partition in k_way_partition( A[1:], k ) : # add new element to one of the current clusters
                for i, cluster in enumerate( partition ) :
                    yield partition[:i] + [ cluster + [ A[0] ] ] + partition[i+1:]
            for partition in k_way_partition( A[1:], k-1 ) : # add new element to a new cluster
                yield [ [ A[0] ] ] + partition
    

    这是主要功能:

    def find_shapes( lines, k ) :
        """
        Looks for a partition of lines into k shapes, and print the solution if there is one.
        """
        for partition in k_way_partition( lines, k ) :
            if all( is_any_closed(cluster) for cluster in partition ) : # we found a solution
                for j, cj in enumerate( partition ) :
                    print "shape {}: {}".format(j, cj )
                break
    

    示例

    让我们生成随机数据并尝试解决方案:

    # square
    lines = [ line( (0,0), (0,1) ) ]
    lines.append( line( (0,1), (1,1) ) )
    lines.append( line( (1,1), (1,0) ) )
    lines.append( line( (1,0), (0,0) ) )
    # triangle
    lines.append( line( (2,2), (2,3) ) )
    lines.append( line( (2,3), (3,2) ) )
    lines.append( line( (3,2), (2,2) ) )
    lines
    
    random.shuffle( lines )  # randomize the order of lines
    for i, l in enumerate( lines ) :
        if random.random() < 0.5 :
            lines[i] = l.reverseLine()  # randomize order of coordinates
    lines
    

    输出[8]:

    [line[(0, 1), (1, 1)],
     line[(1, 1), (1, 0)],
     line[(2, 2), (2, 3)],
     line[(0, 0), (1, 0)],
     line[(3, 2), (2, 2)],
     line[(3, 2), (2, 3)],
     line[(0, 0), (0, 1)]]
    

    现在让我们在随机数据上运行我们的解决方案:

    find_shapes( lines, 2 )
    shape 0: [line[(3, 2), (2, 3)], line[(3, 2), (2, 2)], line[(2, 2), (2, 3)]]
    shape 1: [line[(0, 0), (0, 1)], line[(0, 0), (1, 0)], line[(1, 1), (1, 0)], line[(0, 1), (1, 1)]]
    

    有效!

    【讨论】:

    • 感谢您的帮助,但此方法假定我知道会有多少个形状,而现实是我正在寻找形状而不知道我是否会有任何形状。让它只输入一个行列表会很难吗?
    【解决方案4】:

    与@ChristophHegemann 的想法一样,想象一下数字实际上只是点的表示,所以在我的例子中 Line(1,4) 或 Edge(1,4) 只是点 1 和点 4 之间的一条线。

    from pprint import pprint
    
    class Edge(object):
        def __init__(self,a,b):
            self.a        = a
            self.b        = b
            self.previous = None
            self.next     = None
    
        def __repr__(self):
            return "<Edge(%s,%s)>" % (self.a,self.b)
    
    def find_shapes(lines):
        # builds the graph
        lines  = link_lines(lines)
        # explores the graph
        shapes = extract_shapes(lines)
        return shapes    
    
    def link_lines(lines):
        # keep a safe copy of the list of lines. The original list will be tampered with.
        lines_copy = list(lines)
    
        for L in lines_copy:
            lines.remove(L)
            for line in lines:
                # if current line's end is other line's start
                if L.b == line.a:
                    L.next        = line
                    line.previous = L
    
                # if current line's start is other line's end
                elif L.a == line.b:
                    L.previous = line
                    line.next  = L
    
                # if we found both the previous and the next edge then we're done for this edge.
                # (edge has at most 1 previous and 1 next edge in a given shape).
                # this of course supposes that any single edge is present in at most one shape.
    
                if L.previous and L.next :
                    break
    
            #put L back in
            lines.append(L)
    
        # return newly linked lines (graph)
        return lines
    
    def extract_shapes(lines):
        lines_copy = list(lines)
        shapes     = []
        while lines_copy : 
            L         = lines_copy.pop()
            shape     = [L]
            next_edge = L.next
            # unlike @ChristophHegemann I work with edges, but the algorithm seems 
            # to be the same whether you work with edges or vertices
            while next_edge != L:
                shape.append(next_edge)
                lines_copy.remove(next_edge)            
                next_edge = next_edge.next
    
            shapes.append(shape)
        return shapes
    
    # list of lines
    # let's pretend shapes are : L1,L10,L2,L4 and L5,L3,L7,L11 and L6,L9,L8,L12
    
    L1  = Edge(1,2)
    L10 = Edge(2,3)
    L2  = Edge(3,4)
    L4  = Edge(4,1)
    
    L5  = Edge(5,6)
    L3  = Edge(6,7)
    L7  = Edge(7,8)
    L11 = Edge(8,5)
    
    L6  = Edge(9,10)
    L9  = Edge(10,11)
    L8  = Edge(11,12)
    L12 = Edge(12,9)
    
    # random order of lines 
    lines = [L1,L2,L3,L4,L5,L6,L7,L8,L9,L10,L11,L12]
    
    pprint(find_shapes(lines))
    
    # chaouche@karabeela ~/CODE/TEST/PYTHON/SO $ python lines.py
    # [[<Edge(12,9)>, <Edge(9,10)>, <Edge(10,11)>, <Edge(11,12)>],
    #  [<Edge(8,5)>, <Edge(5,6)>, <Edge(6,7)>, <Edge(7,8)>],
    #  [<Edge(2,3)>, <Edge(3,4)>, <Edge(4,1)>, <Edge(1,2)>]]
    # chaouche@karabeela ~/CODE/TEST/PYTHON/SO $
    

    【讨论】:

      猜你喜欢
      • 2012-04-06
      • 1970-01-01
      • 2013-09-03
      • 2020-10-20
      • 1970-01-01
      • 2015-09-08
      • 2014-04-26
      • 1970-01-01
      • 2020-09-10
      相关资源
      最近更新 更多