背景

雷达图渲染前要统计采样值的分布(算百分位、做增益和对比度映射),这步叫“采样直方图”。打开一个多文件的测线时,每打开一个文件都要算一遍。

原来的 O(N×M)

最早的实现是“对每个待统计的输出桶,遍历一遍所有采样点统计落点”。N 个采样点 × M 个桶,复杂度 O(N×M)。单文件几百万采样点时,这一步就把打开速度拖慢一大截,多文件叠加更明显。

改成 O(N) 单遍桶计数

其实直方图根本不用每个桶都扫一遍数据。一次遍历,把每个采样点直接丢进它对应的桶即可:

fun histogram(samples: IntArray, min: Int, max: Int, bucketCount: Int): IntArray {
val buckets = IntArray(bucketCount)
val range = (max - min).coerceAtLeast(1)
for (v in samples) {
val idx = ((v - min).toLong() * bucketCount / range).toInt()
.coerceIn(0, bucketCount - 1)
buckets[idx]++
}
return buckets
}

一次遍历 O(N) 完事,桶数 M 不再参与复杂度。

效果

多文件雷达图加载速度大幅提升,这步从明显耗时变成几乎可忽略。而且这是个公共算法,数据处理、层位识别、病害标注所有模块都受益,改一处全提速。

小结

直方图、分桶统计这类需求,单遍桶计数是标配。看到“对每个桶扫一遍数据”的实现,基本都可以重写成单遍 O(N)。算法层面的优化往往比堆并发更治本。