C++ C+中数组范围内的有效元素计数+;

C++ C+中数组范围内的有效元素计数+;,c++,arrays,count,range,C++,Arrays,Count,Range,我有一个大的、严格递增的偏移量数组(1000万个整数),用于另一个更大的数据数组。数据中没有大于50的元素。比如说, unsigned char data[70*1000*1000] = {0,2,1,1,0,2,1,4,2, ...}; unsigned int offsets[10*1000*1000] = {0,1,2,4,6,7,8, ...}; 然后,我想在一系列直到运行时才知道的范围中查找每个元素的计数,只包括偏移量包含在offsets数组中的元素。每个范围的端点指的是数据数组的索

我有一个大的、严格递增的偏移量数组(1000万个整数),用于另一个更大的数据数组。
数据中没有大于50的元素。比如说,

unsigned char data[70*1000*1000] = {0,2,1,1,0,2,1,4,2, ...};
unsigned int offsets[10*1000*1000] = {0,1,2,4,6,7,8, ...};
然后,我想在一系列直到运行时才知道的范围中查找每个元素的计数,只包括偏移量包含在
offsets
数组中的元素。每个范围的端点指的是数据数组的索引,而不是偏移量。例如,范围[1,4]的数据为:

1 zero
1 one
1 two
结果只包括一个“一”,因为虽然
数据[3]
数据[2]
都等于一,但
偏移量中不包括3

我需要计算几百个范围内的这些装箱计数,其中一些跨越整个阵列。我考虑遍历数据数组,为每个bin和元素存储一个累积和,但是内存需求会被禁止。以下是我的实现的简单版本:

for(int i=0; i<range_count; i++){
    unsigned int j=0;
    while(j<range_starts[i]) pi++;
    while(j < 10000000 and data[j]<=range_ends[i]) bins[i][data[offsets[j++]]]++;
}

for(inti=0;i当你说偏移量被限制为50时,听起来你已经得到了答案——它们似乎是正整数

为每个数据值(从0到50)的向量建立索引,然后进行其他计算会更便宜。这将是一种反向索引,从数据到数据库条目

所以,你应该:

data[50][...] = {offsets related to the given data value}
计算将针对每个数组执行,检查初始元素,并从一个数组跳到另一个数组,保持最后一个已验证元素的位置

这将与整个数组的元素数成线性关系,乘以搜索范围,乘以数组“数据”中的元素数(0到50),考虑到您需要多次这样做,这不是最好的方法

另一种方法是,对于每个数据项,从0到50,使用一个二叉树,甚至是一个哈希结构,这样您现在就可以知道数据库项标识符是否属于当前数据元素的ID集(从0到50)。在最好的情况下,对于每个iteraction,这在您的搜索范围内是线性的


在分析中,我认为50是一个常数,因此仅在第一个数据数组中搜索,或者在数组的所有50个条目中搜索“data”是相同的。我不确定这是否是一个有效的假设,因此复杂性为:O(nr),n等于数据的最大范围(0到50),r等于搜索范围(数据库中的条目数)。这对每次计算都是有效的,因此,考虑到i是计算数,复杂性将被给出为O(nri)。

当你说偏移量被限制为50时,听起来你已经得到了答案——它们似乎是正整数

为每个数据值(从0到50)的向量建立索引,然后进行其他计算会更便宜。这将是一种反向索引,从数据到数据库条目

所以,你应该:

data[50][...] = {offsets related to the given data value}
计算将针对每个数组执行,检查初始元素,并从一个数组跳到另一个数组,保持最后一个已验证元素的位置

这将与整个数组的元素数成线性关系,乘以搜索范围,乘以数组“数据”中的元素数(0到50),考虑到您需要多次这样做,这不是最好的方法

另一种方法是,对于每个数据项,从0到50,使用一个二叉树,甚至是一个哈希结构,这样您现在就可以知道数据库项标识符是否属于当前数据元素的ID集(从0到50)。在最好的情况下,对于每个iteraction,这在您的搜索范围内是线性的

在分析中,我认为50是一个常数,因此仅在第一个数据数组中搜索,或者在数组的所有50个条目中搜索“data”是相同的。我不确定这是否是一个有效的假设,因此复杂性为:O(nr),n等于数据的最大范围(0到50),r等于搜索范围(数据库中的条目数)。这对每次计算都有效,因此,将i视为计算数,复杂性将给出O(nri)。

这是否可行

(在现场演示)

这行得通

(在现场演示)


虽然鲁本的回答确实将计数时间缩短了一半左右,但对于我的应用程序来说,它仍然太慢了。我在这里为好奇的人提供了我的解决方案

首先,我通过将
数据
数组中未按
偏移量
索引的元素设置为未使用的值(例如51)进行优化。这消除了跟踪偏移量的需要,因为在报告结果时,我可以简单地忽略第51个箱子的内容

虽然我在回答中提到存储每个bin和元素的累积计数需要太多内存,但我能够在线性时间内存储每个bin和范围端点的累积计数。然后,对于每个范围,我通过减去左端po处该元素的累积计数来计算每个元素的出现次数右端点处的计数范围的int。以下是我使用的:

struct range{
    unsigned int lowerbound;
    unsigned int upperbound;
    unsigned int bins[52];
};

struct endpoint{
    int n;
    unsigned int counts[50];
};

range ranges[N_RANGES];
endpoint endpoints[N_RANGES*2];
cumulative_counts[52];

// ... < data manipulation > ... 

endpoint* first_ep = &endpoints[0];
endpoint* last_ep = &endpoints[N_RANGES*2-1];
endpoint* next_ep;

for(next_ep=&endpoints[0];next_ep<last_ep;next_ep++){
    unsigned char* i = &data[next_ep->n];
    unsigned char* i_end = &data[(next_ep+1)->n];
    for(int j=0;j<51;j++) next_ep->counts[j] = cumulative_counts[j];
    while(i<i_end) cumulative_counts[*(i++)]++;
}
for(int i=0;i<51;i++) last_ep->sums[i] = cumulative_counts[i];
for(int i=0;i<N_RANGES;i++){
    while(first_ep->n != ranges[i].lowerbound) first_ep++;
    last_ep = first_ep+1;
    while(last_ep->n != ranges[i].upperbound) last_ep++;
    for(int j=0;j<51;j++) tests[i].bins[j] = end_ep->counts[j]-start_ep->counts[j];
    ranges[i].bins[data[last_ep->n]]++;
}
struct范围{
无符号整数下限;
无符号整数上限;
无符号整型箱[52];
};
结构端点{
int n;
无符号整数计数[50];
};
范围范围[N_范围];
端点[N_范围*2];
累积_计数[52];
//…<数据操作>。。。
端点*first_ep=&端点[0];
端点*last_ep=&端点[N_范围*2-1];
端点*next_ep;
for(next_ep=&端点[0];next_epn];
无符号字符*i_end=&data[(next_ep+1)->n];
对于(int j=0;jcounts[j]=累计_计数[j];
而(在!=范围[i].上界)最后一个_ep++;
对于(int j=0;jcounts[j]-开始->计数[j];
响
struct range{
    unsigned int lowerbound;
    unsigned int upperbound;
    unsigned int bins[52];
};

struct endpoint{
    int n;
    unsigned int counts[50];
};

range ranges[N_RANGES];
endpoint endpoints[N_RANGES*2];
cumulative_counts[52];

// ... < data manipulation > ... 

endpoint* first_ep = &endpoints[0];
endpoint* last_ep = &endpoints[N_RANGES*2-1];
endpoint* next_ep;

for(next_ep=&endpoints[0];next_ep<last_ep;next_ep++){
    unsigned char* i = &data[next_ep->n];
    unsigned char* i_end = &data[(next_ep+1)->n];
    for(int j=0;j<51;j++) next_ep->counts[j] = cumulative_counts[j];
    while(i<i_end) cumulative_counts[*(i++)]++;
}
for(int i=0;i<51;i++) last_ep->sums[i] = cumulative_counts[i];
for(int i=0;i<N_RANGES;i++){
    while(first_ep->n != ranges[i].lowerbound) first_ep++;
    last_ep = first_ep+1;
    while(last_ep->n != ranges[i].upperbound) last_ep++;
    for(int j=0;j<51;j++) tests[i].bins[j] = end_ep->counts[j]-start_ep->counts[j];
    ranges[i].bins[data[last_ep->n]]++;
}