Algorithm 用于查找1';在给定的索引之前

Algorithm 用于查找1';在给定的索引之前,algorithm,data-structures,Algorithm,Data Structures,给定一个由0和1组成的(长)字符串,我需要能够快速回答此类查询:字符串中有多少个1位于给定索引I之前?可以假设1位于索引i处 我正在寻找一种尽可能紧凑的数据结构,它可以为给定的0和1字符串计算一次,然后用作查找表,以快速回答上述查询 背景。在我的特殊情况下,0和1的字符串对栅格地图进行编码(例如在视频游戏中),其中0表示障碍物,1表示可通行的位置。我在数组中存储从所有可通行位置到一个特殊位置的距离。该查询与此对应:给定一个可通过的位置(即0和1字符串的索引),我需要能够快速确定距离数组中的相应索

给定一个由0和1组成的(长)字符串,我需要能够快速回答此类查询:字符串中有多少个1位于给定索引
I
之前?可以假设1位于索引
i

我正在寻找一种尽可能紧凑的数据结构,它可以为给定的0和1字符串计算一次,然后用作查找表,以快速回答上述查询

背景。在我的特殊情况下,0和1的字符串对栅格地图进行编码(例如在视频游戏中),其中0表示障碍物,1表示可通行的位置。我在数组中存储从所有可通行位置到一个特殊位置的距离。该查询与此对应:给定一个可通过的位置(即0和1字符串的索引),我需要能够快速确定距离数组中的相应索引

我正在寻找一种尽可能紧凑的数据结构,它可以为给定的0和1字符串计算一次,然后用作查找表,以快速回答上述查询

这个问题已经有60年的历史了,并且得到了广泛的解决。你所看到的其实只是一个向量,你可以为每个值定义为0,除了1

如果与其他值相比,只有很少的1,只需使用线性代数库中存在的众多稀疏向量表示之一即可

您没有提供足够的信息(例如,您的原始向量是否仍然可用,或者在您拥有数据存储后它是否将被删除?我将假设这一点),但假设这是一项测试,您可以自己解决实际问题,而不是选择正确的库来执行此操作:

知道真正的计算机与他们在基础CS中教授的算法完全不同,最好的存储几乎总是线性存储

由于计数1实际上比将数据从RAM加载到CPU寄存器的时间要短得多,因此这里最有效的选择是最简单的:

获取原始向量值的字长(例如,64),并将其转换为字中的位集(或未设置,如果值!=1);继续下一个单词和原始向量的下一部分

现在,为了计算1的数量,您只需使用一条“总体计数”指令,这条指令几乎是当今所有CPU都有的——例如,SSE4.1作为
POPCNT
引入的nx86(_64)。使用SIMD指令生成相邻单词填充计数的总和,并将其累积到索引/字长点。如果您的问题足够大,并且您有多个具有单独缓存的内核,那么您也可以轻松地将该算法划分为多个并行线程,因为没有相互依赖性。你只要把最后的总数加起来就行了。由于我自己实现了类似的SIMD优化代码,如果您在CPU缓存上受到限制,那么多线程并没有得到回报,因为您最终只能等待具有多个内核的RAM

任何告诉您使用“运行长度”或“链表”实现来编码1之间的距离的人都忽略了一个事实,如前所述,有问题的部分是从RAM获取数据,而不是实际计数。内存控制器总是获取整个内存“行”,而不仅仅是一个值,因此,在等待第一个元素时,可能很容易花费时间来计算数百个字的字长原始值中的1,随后对同一行中的字的访问速度相当快

Bjarne Stroustrup(C++背后的邪恶策划者之一)在年很好地说明了这一点(部分是用看不见的图形)

我正在寻找一种尽可能紧凑的数据结构,它可以为给定的0和1字符串计算一次,然后用作查找表,以快速回答上述查询

这个问题已经有60年的历史了,并且得到了广泛的解决。你所看到的其实只是一个向量,你可以为每个值定义为0,除了1

如果与其他值相比,只有很少的1,只需使用线性代数库中存在的众多稀疏向量表示之一即可

您没有提供足够的信息(例如,您的原始向量是否仍然可用,或者在您拥有数据存储后它是否将被删除?我将假设这一点),但假设这是一项测试,您可以自己解决实际问题,而不是选择正确的库来执行此操作:

知道真正的计算机与他们在基础CS中教授的算法完全不同,最好的存储几乎总是线性存储

由于计数1实际上比将数据从RAM加载到CPU寄存器的时间要短得多,因此这里最有效的选择是最简单的:

获取原始向量值的字长(例如,64),并将其转换为字中的位集(或未设置,如果值!=1);继续下一个单词和原始向量的下一部分

现在,为了计算1的数量,您只需使用一条“总体计数”指令,这条指令几乎是当今所有CPU都有的——例如,SSE4.1作为
POPCNT
引入的nx86(_64)。使用SIMD指令生成相邻单词填充计数的总和,并将其累积到索引/字长点。如果您的问题足够大,并且您有多个具有单独缓存的内核,那么您也可以轻松地将该算法划分为多个并行线程,因为没有相互依赖性。你只要把最后的总数加起来就行了。在我自己实现了类似的SIMD优化代码之后,如果您在CPU缓存上受到限制,多线程并没有得到回报,因为您最终只能等待
[ 1 0 0 1 1 0 1 0 1 1 1 0 0 0 ]
[ - - - - - - - - - - - - - 0 ]
[ - - - - - - - - - - - - 0 0 ]
[ - - - - - - - - - - - 0 0 0 ]
[ - - - - - - - - - - 1 0 0 0 ]
[ - - - - - - - - - 2 1 0 0 0 ]
[ - - - - - - - - 3 2 1 0 0 0 ]
[ - - - - - - - 3 3 2 1 0 0 0 ]
  ...
[ 7 6 6 6 5 5 4 3 3 2 1 0 0 0 ]