Python:表示自我包装的方形网格(圆柱体)

3 投票
2 回答
922 浏览
提问于 2025-04-16 10:28

我正在模拟一个方形网格,这个网格是可以自我环绕的。也就是说,如果你向上走超过最高点,就会回到最低点,就像一个圆柱体一样;如果你向右走,就会碰到边界。我需要跟踪不同代理的位置、各个点的资源数量,并根据一些规则计算代理的移动方向。

那么,最好的建模方法是什么呢?

我是不是应该创建一个表示点的类,这个类里面有方法可以返回每个方向上相邻的点?如果这样做的话,我可能还需要让这个类可以被哈希,这样我才能把它作为字典的键,字典里存放整个网格(我想这样的网格应该用字典来表示吧?)

或者,我应该创建一个描述整个网格的类,而不把每个点当作独立的对象来处理?

又或者,我可以直接使用普通的(x, y)元组,然后在其他地方写方法来查找邻居?

我需要建模的很多内容现在还不太明确。此外,我还预计这个表面的几何形状将来可能会发生变化(例如,它可能会在两个方向上都环绕)。

补充问题:我应该把资源数量的信息附加到每个点的实例上,还是应该有一个单独的类来存放一个以点为索引的资源地图?

2 个回答

1

我需要跟踪不同代理的位置、各个地点的资源数量,并根据一些规则计算代理将要移动的方向。

听起来这就像是在说图(graph),不过我总是试着把每个问题都看成图。你提到的所有操作(移动、存储资源、找出要移动到哪里)在图中都是非常常见的。你还可以很容易地改变图的形状,比如从圆柱体变成环面,或者其他任何形状。

唯一的问题是,这种表示方式需要占用比其他方式更多的空间。

好的一面是,你可以使用一个图形库来创建这个图,甚至可能用一些图算法来计算代理的移动方向。

2

如果你想要一个可以哈希的 Point 类,而且不想花太多功夫,可以通过继承元组(tuple)来实现,并添加你自己的邻居方法。

class Point(tuple):
    def r_neighbor(self):
        return Point((self[0] + 1, self[1]))
    def l_neighbor(self): 
        [...]

x = Point((10, 11))
print x
print x.r_neighbor()

元组的构造函数需要一个可迭代的对象,所以在使用 Point((10, 11)) 时要用双括号;如果你想避免这样做,可以重写 __new__ 方法(重写 __init__ 是没有意义的,因为元组是不可变的):

def __new__(self, x, y):
    return super(Point, self).__new__(self, (x, y))

这里也可以用到模运算——不过这要看你具体要做什么:

def __new__(self, x, y, gridsize=100):
    return super(Point, self).__new__(self, (x % gridsize, y % gridsize))

或者可以支持任意维度的网格,然后在 __new__ 中继续使用元组:

def __new__(self, tup, gridsize=100):
    return super(Point, self).__new__(self, (x % gridsize for x in tup))

关于你提到的资源问题:由于 Point 是一个不可变的类,所以不太适合用来存储可能会改变的信息。使用 defaultdict 会比较方便;你不需要手动初始化它。

from collections import defaultdict

grid = defaultdict(list)
p = Point((10, 13))
grid[(10, 13)] = [2, 3, 4]
print grid[p]                  # prints [2, 3, 4]
print grid[p.r_neighbor]       # no KeyError; prints []

如果你想要更多的灵活性,可以在 defaultdict 中使用字典而不是列表;但是 defaultdict(defaultdict) 是不行的;你需要创建一个新的 defaultdict 工厂函数。

def intdict():
    return defaultdict(int)

grid = defaultdict(intdict)

或者更简洁地说

grid = defaultdict(lambda: defaultdict(int))

然后

p = Point((10, 13))
grid[(10, 13)]["coins"] = 50
print grid[p]["coins"]              # prints 50
print grid[p.r_neighbor]["coins"]   # prints 0; again, no KeyError

撰写回答