给定一个0和1的(长)字符串,我需要能够快速回答这种类型的查询:字符串中有多少1位于给定索引i
之前?可以假设1位于索引i
。
我正在寻找一个尽可能紧凑的 一个数据结构,可以为给定的0和1字符串计算一次,然后用作查找表快速回答查询如上所述。
背景。在我的特定情况下,0和1的字符串对网格地图进行编码(例如在视频游戏中),其中0表示障碍物,1表示可通行位置。我将所有 passable 位置的距离存储到数组中的一个特殊位置。查询对应于此:给定一个可通过的位置(即0和1的字符串的索引),我需要能够快速确定距离数组中的相应索引。
答案 0 :(得分:1)
我正在寻找一个尽可能紧凑的数据结构,可以为给定的0和1的字符串计算一次,然后用作查找表来快速回答查询如上所述。
这个问题大约有六十年之久,并且得到了广泛的解决。您正在查看的内容实际上只是一个向量,您可以为每个值定义为0,但为1。
如果与其他值相比只有非常小的1,那么只需使用线性代数库中的许多稀疏矢量表示之一。
您没有提供足够的信息(比如,您的原始矢量是否仍然可用,或者您的数据存储后它是否会被删除?我会假设这一点),但假设这是一个自己解决现实世界问题的测试,而不是选择合适的库来执行此操作:
了解真正的计算机并不像他们在基本CS中教授的算法进行了优化,最佳存储几乎总是线性存储。
因为计数实际上比将数据从RAM加载到CPU寄存器要少得多,所以这里最有效的选择是最简单的:
获取原始矢量值的字长(例如,64),并将它们转换为单词中设置的位(或不设置,如果值!= 1);继续下一个单词和原始向量的下一部分。
现在,要评估一个数量,你只需使用一个"人口数"现在几乎所有CPU都有的指令 - 例如,由SSE4.1引入n x86(_64)为POPCNT
。使用SIMD指令生成相邻单词总数计数的总和,并将它们累积到索引/字长的点。如果您的问题足够大并且您有多个具有单独缓存的核心,您也可以轻松地将该算法划分为多个并行线程,因为它们没有相互依赖性。你只需在最后加上总和。我自己实现了类似的SIMD优化代码,如果您限制CPU缓存,多线程不会得到回报,因为您最终会等待具有多个内核的RAM。
任何人告诉你使用" runlength"或"链接列表"对1s之间的距离进行编码的实现忽略了这样一个事实:如上所述,有问题的部分是从RAM获取数据,而不是实际计数。内存控制器总是获取整个内存" row"而不仅仅是单个值,这样在等待第一个元素时可能很容易花费时间来计算单词长度为几百个单词的1s每个值,对来自同一行的单词的后续访问非常快。
在this short lecture中,Bjarne Stroustrup(是C ++背后的邪恶策划者之一)很好地说明了(部分是隐形图)。
答案 1 :(得分:1)
你正在寻找“简洁的可索引字典”,这些字典也被许多其他名字所知 - 你也可以谷歌“简洁的排名选择”。最佳解决方案具有约5%的开销和恒定时间查找。
“未压缩比特序列中的空间高效,高性能等级和选择结构”,作者:Dong Zhou,David G. Andersen,Michael Kaminsky
答案 2 :(得分:0)
编辑 意识到我已经回答了你问题的反面;这告诉你你的位置有多少1。只需前进位图,而不是我在下面建议的内容。
尝试创建位图长度的int数组。向后工作,总结你到目前为止所看到的1的数量;例如
.bash_profile
给出
[ 1 0 0 1 1 0 1 0 1 1 1 0 0 0 ]
现在它只是一个数组查找,还有一个额外的好处,即如果你想知道任意两点之间的数字,你可以通过从另一个中减去它来解决它。