如何在Python中实现类似Gurobi的高效检索tuplelist类
类似Gurobi tuplelist类的实现方案
核心原理
Gurobi的tuplelist高效的核心是提前构建倒排索引避免全表扫描,具体实现逻辑如下:
- 继承Python原生
list类,保留列表所有原生操作能力,无需重写基础列表方法 - 内部维护倒排索引字典,键为
(字段位置, 字段值)二元组,值为所有符合该位置等于该值的元组在列表中的下标集合 select方法通过匹配多个字段的倒排索引取交集,直接定位符合条件的元素下标,查询效率仅和匹配结果数量相关,不受总数据量影响
完整实现代码
class TupleList(list): def __init__(self, iterable=None): super().__init__() self._index = {} # 倒排索引存储结构:(位置, 值) -> 对应元素下标集合 self._tuple_length = None if iterable: for item in iterable: self.append(item) def append(self, item): # 校验元素类型为元组 if not isinstance(item, tuple): raise TypeError("TupleList仅支持存储元组类型元素") # 校验所有元组长度统一 if self._tuple_length is None: self._tuple_length = len(item) elif len(item) != self._tuple_length: raise ValueError(f"所有元组长度必须统一为{self._tuple_length}") # 元素加入列表 current_idx = len(self) super().append(item) # 更新倒排索引 for pos, val in enumerate(item): index_key = (pos, val) if index_key not in self._index: self._index[index_key] = set() self._index[index_key].add(current_idx) def select(self, *args): # 校验查询参数数量和元组长度匹配 if len(args) != self._tuple_length: raise ValueError(f"查询参数数量需与元组长度一致,应为{self._tuple_length}个") # 收集所有非通配条件的匹配下标集合 match_collections = [] for pos, condition in enumerate(args): if condition is None: # None为通配符,匹配该位置所有值 continue # 支持单值匹配或多值匹配 target_values = {condition} if not isinstance(condition, (list, tuple, set)) else set(condition) # 合并该位置所有目标值对应的下标 pos_match_set = set() for val in target_values: pos_match_set.update(self._index.get((pos, val), set())) match_collections.append(pos_match_set) # 无有效查询条件则返回全量数据 if not match_collections: return self.copy() # 取所有条件集合的交集得到最终匹配下标 result_indices = set.intersection(*match_collections) # 按原列表顺序返回匹配结果 return [self[idx] for idx in sorted(result_indices)]
使用示例
# 初始化TupleList实例 tl = TupleList([ (1, 2, 3), (1, 4, 5), (2, 2, 6), (1, 2, 7), (3, 4, 8) ]) # 示例1:查询第一个位置为1,第二个位置为2,第三个位置任意的元组 print(tl.select(1, 2, None)) # 输出:[(1, 2, 3), (1, 2, 7)] # 示例2:查询第二个位置为4,第三个位置为5或8的元组 print(tl.select(None, 4, [5, 8])) # 输出:[(1, 4, 5), (3, 4, 8)] # 支持原生列表的所有操作,新增元素会自动更新索引 tl.append((2, 4, 9)) print(tl.select(2, None, None)) # 输出:[(2, 2, 6), (2, 4, 9)]
效率说明
该实现和Gurobi官方tuplelist的核心逻辑完全一致,在十万级以上元组的查询场景下,查询效率比原生列表推导高两个数量级以上。如果需要支持pop、remove等列表操作,只要对应重写方法同步更新倒排索引即可。
内容的提问来源于stack exchange,提问作者Li Mike
相关产品推荐
相关产品推荐

