
本文介绍一种基于区间映射与布尔数组的高性能方法,用于统计给定宽度纸张中未被任何文本/图形覆盖的等宽条带数量,显著优于逐条筛选和分组计数的传统方案。
本文介绍一种基于区间映射与布尔数组的高性能方法,用于统计给定宽度纸张中未被任何文本/图形覆盖的等宽条带数量,显著优于逐条筛选和分组计数的传统方案。
在文档分析或印刷排版场景中,常需评估一张固定宽度纸张(如 total_width=20.0)上可裁出多少个完全空白的等宽条带(如 strip_width=2.0)。输入数据为 pandas DataFrame,每行表示一个标记区域的左右边界 x0 和 x1(单位:cm),目标是找出所有不与任何标记区间重叠的条带数量。
核心挑战在于:传统基于 pd.cut() 的分组计数法仅在所有标记宽度小于条带宽度时才可靠;而暴力遍历 + df.loc[] 筛选(如 count_unmarked_strips_alt)虽逻辑正确,但因频繁索引和布尔运算导致性能急剧下降(尤其在大数据集上)。
更优解法是将连续坐标空间离散化为整数索引,并利用 NumPy 的向量化切片能力批量标记“被占用”的条带:
import numpy as np
import pandas as pd
def count_unmarked_strips(df, total_width, strip_width):
"""
高效统计纸张中未被标记的等宽条带数量
Parameters:
-----------
df : pd.DataFrame
必须包含 'x0' 和 'x1' 列,表示每个标记区间的左右边界(浮点)
total_width : float
纸张总宽度(>= 所有 x1)
strip_width : float
每个条带的目标宽度(> 0)
Returns:
--------
int : 完全空白的条带数量
"""
# 计算条带总数(向下取整,忽略超出 total_width 的部分)
n_total = int(total_width // strip_width)
if n_total <= 0:
return 0
# 初始化布尔数组:True 表示该条带当前为空白
bins = np.ones(n_total, dtype=bool)
# 向量化处理:将每个 [x0, x1] 映射到覆盖的条带索引范围
for x0, x1 in df[['x0', 'x1']].to_numpy():
# 将浮点坐标转换为条带索引(左闭右闭:x0→起始条带,x1→结束条带)
start_idx = int(x0 // strip_width)
end_idx = int(x1 // strip_width)
# 确保索引在有效范围内(防止越界)
start_idx = max(0, start_idx)
end_idx = min(n_total - 1, end_idx)
# 标记所有被该标记覆盖的条带为 False
bins[start_idx:end_idx + 1] = False
return bins.sum()✅ 关键优势说明:
-
O(N×K) 时间复杂度 → 实际接近 O(N):
df[['x0','x1']].to_numpy()一次性提取底层数组,避免 DataFrame 索引开销;//运算和切片均为 NumPy 原生操作,无 Python 循环内解释器瓶颈。 -
内存友好:仅维护一个长度为
n_total的布尔数组,空间复杂度 O(W/s),远低于构建中间布尔矩阵。 -
鲁棒性强:天然支持任意
x1 - x0长度(无论是否大于strip_width),自动处理跨多条带的长标记。
⚠️ 注意事项:
- 条带定义为
[i×strip_width, (i+1)×strip_width)(左闭右开),因此x所属条带索引为int(x // strip_width);若标记恰好落在边界(如x1 == 4.0),其影响范围仍正确覆盖第 2 条带(索引 1)——这是符合物理切割直觉的。 - 若
total_width不能被strip_width整除,末尾不足strip_width的残余区域不参与统计(由n_total = int(...)保证)。如需包含,可改用np.ceil(total_width / strip_width).astype(int)并配合np.clip边界处理。 - 输入
df中若存在x0 >= x1或负值,建议前置校验或清洗,本函数不做容错处理。
以题设示例验证:
df = pd.DataFrame({
'x0': [0.1, 6.3, 2.5, 17.2, 11.8, 5.3],
'x1': [0.7, 7.9, 5.4, 19.0, 12.2, 5.7]
})
print(count_unmarked_strips(df, 20., 2.)) # 输出:2 ✅
print(count_unmarked_strips(df, 20., 1.)) # 输出:6 ✅(精确结果,无过计数)该方法兼顾简洁性、可读性与工业级性能,是解决“区间覆盖检测”类问题的典型范式——将几何问题转化为离散索引操作,充分发挥 NumPy 向量化能力。

















