【问题标题】:Python 3.3.2 - 'Grouping' System with CharactersPython 3.3.2 - 带有字符的“分组”系统
【发布时间】:2013-09-02 08:27:18
【问题描述】:

我有一个有趣的小问题。

我需要计算文件中字符“组”的数量。假设文件是​​...

..##.#..#
##..####.
.........
###.###..
##...#...

然后代码将计算# 组的数量。例如,上面将是3。它包括对角线。到目前为止,这是我的代码:

build = []
height = 0
with open('file.txt') as i:
  build.append(i)
  height += 1
length = len(build[0])
dirs = {'up':(-1, 0), 'down':(1, 0), 'left':(0, -1), 'right':(0, 1), 'upleft':(-1, -1), 'upright':(-1, 1), 'downleft':(1, -1), 'downright':(1, 1)}

def find_patches(grid, length):
  queue = []
  queue.append((0, 0))
  patches = 0
  while queue:
    current = queue.pop(0)
    line, cell = path[-1]
    if ## This is where I am at. I was making a pathfinding system.

【问题讨论】:

    标签: python string python-3.x


    【解决方案1】:

    这是我想出的一个幼稚的解决方案。最初我只是想遍历所有元素一次检查每个元素,如果我可以将它放入现有组中。然而这并没有奏效,因为某些组只是稍后才合并(例如,在第二行中的第二个 # 被处理之前,第二行中的第一个 # 不属于大组)。所以我开始研究一种合并算法,然后我想我可以从一开始就这样做。

    所以现在它的工作原理是我将每个 # 放入自己的组中。然后我继续查看两组的组合,并检查它们是否彼此足够接近以至于它们属于同一组。如果是这种情况,我将它们合并并重新开始检查。如果我完全查看了所有可能的组合并且无法再合并,我知道我已经完成了。

    from itertools import combinations, product
    def canMerge (g, h):
        for i, j in g:
            for x, y in h:
                if abs(i - x) <= 1 and abs(j - y) <= 1:
                    return True
        return False
    
    def findGroups (field):
        # initialize one-element groups
        groups = [[(i, j)] for i, j in product(range(len(field)), range(len(field[0]))) if field[i][j] == '#']
    
        # keep joining until no more joins can be executed
        merged = True
        while merged:
            merged = False
            for g, h in combinations(groups, 2):
                if canMerge(g, h):
                    g.extend(h)
                    groups.remove(h)
                    merged = True
                    break
    
        return groups
    
    # intialize field
    field = '''\
    ..##.#..#
    ##..####.
    .........
    ###.###..
    ##...#...'''.splitlines()
    groups = findGroups(field)
    
    print(len(groups)) # 3
    

    【讨论】:

    • 完美!它完美地工作。但是,一个调整,你忘了把return groups放在findGroups的while循环下面。
    • 对,我只是在最后一刻才把它做成一个单独的函数^^
    • 虽然这种蛮力方法适用于小领域,但对于较大的领域可能会变得非常缓慢。我的答案中的算法效率更高。
    • @Blckknght 因此我的“幼稚解决方案”警告:)
    • 我注意到了这一点。我使用计时器模块用 1,000 个测试用例轰炸了您的解决方案,但速度有点慢。否则,一个非常容易理解的解决方案!
    【解决方案2】:

    我不确定您的代码要做什么。您的with 语句打开一个文件,但您所做的只是在with 结束之前将文件对象附加到一个列表中并且它被关闭(其内容永远不会被读取)。我怀疑他不是你想要的,但我不确定你的目标是什么。

    如果我正确理解您的问题,您正在尝试计算图表的connected components。在这种情况下,图形的顶点是“#”字符,边是这些字符在任何方向(水平、垂直或对角线)上彼此相邻的位置。

    有非常简单的算法可以解决这个问题。一种是使用disjoint set 数据结构(也称为“联合查找”结构,因为unionfind 是它支持的两个操作)将'#' 字符组连接在一起,因为它们是从文件中读入。

    这是我不久前为回答另一个问题而写的一个相当小的不相交集:

    class UnionFind:
        def __init__(self):
            self.rank = {}
            self.parent = {}
    
        def find(self, element):
            if element not in self.parent: # leader elements are not in `parent` dict
                return element
            leader = self.find(self.parent[element]) # search recursively
            self.parent[element] = leader # compress path by saving leader as parent
            return leader
    
        def union(self, leader1, leader2):
            rank1 = self.rank.get(leader1,1)
            rank2 = self.rank.get(leader2,1)
    
            if rank1 > rank2: # union by rank
                self.parent[leader2] = leader1
            elif rank2 > rank1:
                self.parent[leader1] = leader2
            else: # ranks are equal
                self.parent[leader2] = leader1 # favor leader1 arbitrarily
                self.rank[leader1] = rank1+1 # increment rank
    

    以下是如何将它用于您的问题,使用 x, y 元组作为节点:

    nodes = set()
    groups = UnionFind()
    
    with open('file.txt') as f:
        for y, line in enumerate(f): # iterate over lines
            for x, char in enumerate(line): # and characters within a line
                if char == '#':
                    nodes.add((x, y)) # maintain a set of node coordinates
    
                    # check for neighbors that have already been read
                    neighbors = [(x-1, y-1), # up-left
                                 (x, y-1),   # up
                                 (x+1, y-1), # up-right
                                 (x-1, y)]   # left
                    for neighbor in neighbors:
                        if neighbor in nodes:
                            my_group = groups.find((x, y))
                            neighbor_group = groups.find(neighbor)
                            if my_group != neighbor_group:
                                groups.union(my_group, neighbor_group)
    
    # finally, count the number of unique groups
    number_of_groups = len(set(groups.find(n) for n in nodes))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-02
      • 2023-04-09
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 2011-04-15
      • 2018-08-07
      相关资源
      最近更新 更多