在Python中,我们需要处理一个大型矩阵,尺寸为250x250x30,总共包含1,875,000个单元格。我们需要为每个单元格设置任意数量的标记,并希望以一种易于使用且空间效率高的方式来实现。
最初的解决方案是使用一个250x250x30的列表数组,每个元素都是一个字符串数组,例如:[“FLAG1”,“FLAG8”,“FLAG12”]。后来,为了节省空间,改为只存储整数:[1,8,12]。这些整数通过getter/setter函数内部映射到原始的标记字符串。即使每个点有8个标记,这也只使用250mb的内存,在内存方面是可接受的。
问题是:是否存在其他明显的方法来组织此类数据?
2、解决方案
解决方案1:使用字典
当每个单元格都有一个标记时,上述解决方案是可行的。但是,如果我们处理的是一个稀疏数据集,其中只有矩阵中的一小部分单元格有标记,那么使用字典会更合适。字典的键可以是单元格位置的元组,值可以是标记的列表,如下所示:
allFlags = {(1,1,1):[1,2,3], (250,250,30):[4,5,6]}
在这个例子中,单元格 (1,1,1) 有标记 1, 2, 3,而单元格 (250,250,30) 有标记 4, 5, 6。
解决方案2:使用位掩码
我们可以定义一些常量,每个常量都有不同的、2的幂的值,如下所示:
FLAG1 = 0x01
FLAG8 = 0x02
FLAG12 = 0x04
...
然后,我们可以使用布尔逻辑将这些标记存储在一个整数中,例如:
flags = FLAG1 | FLAG8
要检查一个标记是否已启用,我们可以使用 & 运算符,如下所示:
flag1_enabled = flags & FLAG1
如果标记已启用,此表达式将返回一个非零值,在任何布尔运算中评估为 True。如果标记已禁用,则该表达式将返回 0,在布尔运算中评估为 False。
解决方案3:使用NumPy
如果我们能够使用NumPy库,那么我们可以使用NumPy数组来存储标记。NumPy数组比Python内置的列表更加紧凑和高效,特别适用于处理大量同质数据。我们可以创建一个短整型NumPy数组,将其初始化为0,然后使用getter/setter函数来设置和读取每个单元格的标记,如下所示:
import numpy as np
(x, y, z) = (250, 250, 30)
array = np.zeros((x, y, z), dtype=np.int16)
def setFlag(location, flag):
array[location] |= flag
def unsetFlag(location, flag):
array[location] &= ~flag
解决方案4:使用飞量模式
飞量模式是一种设计模式,它可以让我们共享对象,而不是为每个对象都创建一个新实例。这可以节省内存,并提高性能。在我们的场景中,我们可以使用飞量模式来共享单元格的属性,如下所示:
class Cell:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.z == other.z
def __hash__(self):
return hash((self.x, self.y, self.z))
class Flags:
def __init__(self):
self.flags = {}
def setFlag(self, cell, flag):
if cell not in self.flags:
self.flags[cell] = set()
self.flags[cell].add(flag)
def unsetFlag(self, cell, flag):
if cell in self.flags and flag in self.flags[cell]:
self.flags[cell].remove(flag)
def hasFlag(self, cell, flag):
return cell in self.flags and flag in self.flags[cell]
解决方案5:使用位集
位集是一种数据结构,它可以存储多个标志,而只需要使用一个固定大小的整数。这比使用多个布尔变量或整数更节省空间。在Python中,我们可以使用bitset模块来使用位集。
import bitset
flags = bitset.bitset()
# 设置标记
flags[3] = True
flags[5] = True
# 读取标记
if flags[3]:
print("Flag 3 is set")
if flags[5]:
print("Flag 5 is set")
解决方案6:使用集合
集合是一种数据结构,它可以存储不重复的元素。我们可以使用集合来存储每个单元格的标记,如下所示:
flags = set()
# 设置标记
flags.add((34, 201, 3)) # 设置标记 3 在位置 (34, 201)
# 读取标记
if (3, 2, 1) in flags:
print("Flag 1 is set at position (3, 2)")
我们可以通过定义一个类来封装集合,使其更容易使用,如下所示:
class Flags:
def __init__(self):
self.data = set()
def add(self, x, y, flag):
self.data.add((x, y, flag))
def remove(self, x, y, flag):
self.data.remove((x, y, flag))
def contains(self, x, y, flag):
return (x, y, flag) in self.data
我们可以使用这个类来轻松地设置、读取和删除标记。