简体   繁体   English

Python中的地图颜色算法

[英]Map Color Algorithm in Python

I have a 2D array in Python (version 3.2) that looks like this: 我在Python(3.2版)中有一个2D数组,如下所示:

...AAA....
...AAABB..
..BBBBBCC.
.....CCCC.
.DDD..CC..
.DDD......

It represents a kind of map with areas painted different colors. 它代表一种地图,其区域涂有不同的颜色。 The above example shows four distinct regions, A, B, C, and D. 上面的示例显示了四个不同的区域A,B,C和D。

Here's an example of indexing the array: map[1][5] == 'A' would return True. 这是为数组建立索引的示例:map [1] [5] =='A'将返回True。

I'm trying to write a function that takes in an array like this, and a row/col index, and returns the number of adjoining spaces that are of the same "color". 我正在尝试编写一个函数,该函数接受这样的数组和行/列索引,并返回具有相同“颜色”的相邻空格的数量。 So using that example above, here are some return values (the arguments are the array, row, and column number respectively: 因此,使用上面的示例,这里有一些返回值(参数分别是数组,行和列号:

6 <-- countArea(map, 5, 2)
8 <-- countArea(map, 2, 8)

I'd like to implement this as a recursive function, but I can't figure it out. 我想将其实现为递归函数,但我无法弄清楚。 Here's what I have so far: 这是我到目前为止的内容:

def countArea(map, row, col):

    key = map[row][col]

    if (map[row-1][col] == key):
        return 1 + countArea(map, row-1, col)
    elif (map[row+1][col] == key):
        return 1 + countArea(map, row+1, col)
    elif (map[row][col+1] == key):
        return 1 + countArea(map, row, col+1)
    elif (map[row][col-1] == key):
        return 1 + countArea(map, row, col-1)
    else:
        return 1

I know I'm missing something basic here. 我知道我在这里缺少一些基本知识。 I'm basically saying "here is the current character, now look in each direction to see if it has the same character." 我基本上是说“这是当前字符,现在从各个方向看一下它是否具有相同的字符。”

My question is, what am I missing in this recursive definition? 我的问题是,在此递归定义中我缺少什么?

Thanks for your help. 谢谢你的帮助。

My question is, what am I missing in this recursive definition? 我的问题是,在此递归定义中我缺少什么?

Once a grid square has been counted, it must not be counted again (this includes counting by recursive invocations of countArea() !) 一旦对网格平方进行了计数,就不能再次对其进行计数(这包括通过对countArea()的递归调用进行计数!)

Your current algorithm goes as far north as it can, and then keeps taking one step to the south followed by one step to the north. 您当前的算法会尽可能向北移动,然后继续向南走一步,然后向北走一步。 This two-step sequence repeats until you run out of stack space. 重复执行此两步序列,直到堆栈空间用完。

If you like, you could read up on algorithms for this problem in Wikipedia . 如果愿意,您可以在Wikipedia上阅读有关此问题的算法。

In your code the algorithm would look one field left of a given input field and in the recursive call would again call the function on the initial field. 在您的代码中,算法将在给定输入字段的左侧查找一个字段,而在递归调用中,算法将再次在初始字段上调用该函数。 (What you obviously don't want since it would lead to an infinite recursion) (您显然不想要什么,因为它会导致无限递归)

Approach 1 方法1

A method to overcome this problem while still using recursion would be to specify a direction where the recursion should look for more fields of the same type. 在仍然使用递归的情况下克服此问题的一种方法是指定一个方向 ,在该方向上递归应查找更多相同类型的字段。 For example the call to the field directly north (or above ) of the initial one could look recursively farer to the north or east (or right ), the one to the east go south ( below ) and east and so on. 例如,对第一个字段正北(或上方 )的调用可能会递归地看起来更北或向东(或向右 ),向东的字段向南( 朝下 )和向东,依此类推。

By intelligently choosing the first step you can ensure, that there is no overlap in the scanned regions. 通过明智地选择第一步,您可以确保在扫描区域中没有重叠。 However it needs some adaptions to specify the directions the recursive call should scan. 但是,它需要一些调整以指定递归调用应扫描的方向。 BUT: Note that this algorithm would not work if the area is overhanging so if not every field northeast of the starting point can be reached by just moving right and up. 但是:请注意,如果该区域悬空,则此算法将不起作用,因此,如果不能仅通过向右上方移动就可以到达起点东北方的每个字段。

There exist more algorithms like this that are also capable to solve the the mentioned problem. 存在更多类似这样的算法,它们也能够解决上述问题。 Have a look at Flood Filling on wikipedia . 在Wikipedia上查看“ 洪水填充”

Approach 2 方法2

You can also save the already visited fields in some way and directly return from the recursive call if the field was already visited. 您还可以以某种方式保存已访问的字段,如果该字段已被访问,则可以直接从递归调用中返回。

The following implementation should work: 以下实现应起作用:

def countArea(map, row, col, key=None, seen=None):
    if key is None:
        key = map[row][col]
    if seen is None:
        seen = set()
    seen.add((row, col))  # mark this location as visited
    n = 1
    for dy, dx in [(0, 1), (1, 0), (-1, 0), (0, -1)]:
         r, c = row + dy, col + dx
         if r < 0 or r >= len(map) or c < 0 or c >= len(map[0]):  # check boundaries
             continue
         # only increment and recurse if key matches and we haven't already visited
         if map[r][c] == key and (r, c) not in seen:
             n += countArea(map, r, c, key, seen)
    return n

Example: 例:

>>> print '\n'.join(''.join(row) for row in map)
...AAA....
...AAABB..
..BBBBBCC.
.....CCCC.
.DDD..CC..
.DDD......
>>> countArea(map, 5, 2)
6
>>> countArea(map, 2, 8)
8

Note that this assumes that areas with the same key that are only touching at a diagonal should be considered separate, for example for the following map countArea(map, 0, 0) and countArea(map, 1, 1) would both return 1: 请注意,这假设只将具有相同按键的区域仅在对角线上触摸,例如对于以下地图countArea(map, 0, 0)countArea(map, 1, 1)都将返回1:

A.
.A

As a side note, you should not use map as a variable name, as it will mask the builtin map() function. 附带说明一下,您不应将map用作变量名,因为它将掩盖内置的map()函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM