Algorithm 如何阅读所有1';以1';s和0';s随机分布在整个阵列中
我有一个数组,数组中随机分布着1和0Algorithm 如何阅读所有1';以1';s和0';s随机分布在整个阵列中,algorithm,data-structures,Algorithm,Data Structures,我有一个数组,数组中随机分布着1和0 int arr[N] = {1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1....................N} 现在我想尽快检索数组中的所有1,但条件是我不应该丢失数组的确切位置(基于索引),因此排序选项无效。 所以剩下的唯一选择就是线性搜索,也就是O(n),还有比这更好的吗 线性扫描背后的主要问题是,我甚至需要运行扫描 十次。所以我觉得我需要一些其他的数据结构 一旦第一次线性扫描发生,它就会维护这个
int arr[N] = {1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1....................N}
现在我想尽快检索数组中的所有1,但条件是我不应该丢失数组的确切位置(基于索引),因此排序选项无效。
所以剩下的唯一选择就是线性搜索,也就是O(n),还有比这更好的吗
线性扫描背后的主要问题是,我甚至需要运行扫描
十次。所以我觉得我需要一些其他的数据结构
一旦第一次线性扫描发生,它就会维护这个列表
我不需要一次又一次地运行线性扫描
我只需要找到数组中某个范围内的1个数,确切地说,我需要找到数组中40-100范围内的1个数。所以这可能是随机范围,我需要找到这个范围内的1的计数。由于不同的范围要求,我无法进行“求和”和“全部”,因为我需要反复迭代数组我很惊讶您认为排序是线性搜索的一种更快的替代方法 如果你不知道它们发生在哪里,那么没有比线性搜索更好的方法了。如果使用bits或
char
数据类型,您可能会进行一些优化,但这取决于您希望如何使用它
在这方面可以做的最佳优化是克服分支预测。因为每个值都是零或一,所以可以使用它来提升用于存储一个索引的数组的索引
简单方法:
int end = 0;
int indices[N];
for( int i = 0; i < N; i++ )
{
if( arr[i] ) indices[end++] = i; // Slow due to branch prediction
}
(我在上面设置了偏移量arr
,因此它排列得更好)
现在,您可以在O(1)时间内计算任意范围内的1的数量。要计算索引A
和B
之间的1数,只需执行以下操作:
int num = count[B+1] - count[A];
显然,您仍然可以使用非分支预测版本最初生成计数。与对每个查询求和的天真方法相比,所有这些都会给您带来相当好的加速:
int *count = new int[N+1];
int total = 0;
count[0] = 0;
for( int i = 0; i < N; i++ )
{
total += arr[i];
count[i+1] = total;
}
// to compute the ranged sum:
int range_sum( int *count, int a, int b )
{
if( b < a ) return range_sum(b,a);
return count[b+1] - count[a];
}
int*count=新的int[N+1];
int-total=0;
计数[0]=0;
对于(int i=0;i
如果:
n
值m
时间数组中查找1的数量m
次做得更好
正如其他人所指出的,当你第一次需要1的数量时,你肯定要检查每个细胞。但是,如果随后将1的数量存储在变量中(例如sum
),并跟踪对数组的更改(例如,要求所有数组更新都通过特定的update()
函数进行),则每当数组中的0
被替换为1
,则更新()
函数可以将1
添加到sum
中,并且每当数组中的1
被0
替换时,update()
函数可以从sum
中减去1
因此,sum
在第一次对数组中的1进行计数后始终是最新的,不需要进一步计数
(编辑以考虑更新的问题)
如果需要返回数组给定范围内的1数,可以使用比我刚才描述的略为复杂的缓存策略来完成
您可以在数组的每个子集中保留1的计数,并在该子集中将0更改为1时更新相关的子集计数,反之亦然。在数组中查找给定范围内的1总数将是将该范围内完全包含的每个子集中的1数相加,然后计算该范围内但尚未计算的子集中的1数
根据具体情况,可能值得采用一种分层安排,其中(比如)整个数组中的1的数量位于分层的顶部,数组中每个1/q th
中的1的数量位于分层的第二级,每个1/(q^2)中的1的数量数组的th
位于层次结构的第三级,等等。例如,对于q=4,您将在顶部显示1的总数,在第二级显示数组每四分之一中的1的数量,在第三级显示数组每十六分之一中的1的数量,等等。您是否使用C(或派生语言)?如果是,您可以控制数组的编码吗?例如,如果可以使用位图进行计数。位图的好处在于,您可以使用查找表对计数进行求和,尽管如果子范围结尾不能被8整除,您必须特别处理结尾部分字节,但加速效果将非常显著
如果不是这样,你能至少把它们编码成单个字节吗?在这种情况下,如果存在稀疏性(更具体地说,希望经常有多个索引的零条),您可能能够利用稀疏性
因此:
u8 input = {1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1....................N};
您可以编写以下内容(未经测试):
uint countBytesBy1FromTo(u8*输入,uint启动,uint停止)
{//一次计算一个字节的函数,用于小于4的范围,
//在更长的范围内使用以下功能
//假设只有1和0,否则我们必须测试/分支
单位和;
u8*结束=输入+停止;
对于(u8*each=输入+开始;each<结束;each++)
总数+
int num = count[B+1] - count[A];
int *count = new int[N+1];
int total = 0;
count[0] = 0;
for( int i = 0; i < N; i++ )
{
total += arr[i];
count[i+1] = total;
}
// to compute the ranged sum:
int range_sum( int *count, int a, int b )
{
if( b < a ) return range_sum(b,a);
return count[b+1] - count[a];
}
u8 input = {1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1....................N};
uint countBytesBy1FromTo(u8 *input, uint start, uint stop)
{ // function for counting one byte at a time, use with range of less than 4,
// use functions below for longer ranges
// assume it's just one's and zeros, otherwise we have to test/branch
uint sum;
u8 *end = input + stop;
for (u8 *each = input + start; each < end; each++)
sum += *each;
return sum;
}
countBytesBy8FromTo(u8 *input, uint start, uint stop)
{
u64 *chunks = (u64*)(input+start);
u64 *end = chunks + ((start - stop) >> 3);
uint sum = countBytesBy1FromTo((u8*)end, 0, stop - (u8*)end);
for (; chunks < end; chunks++)
{
if (*chunks)
{
sum += countBytesBy1FromTo((u8*)chunks, 0, 8);
}
}
}