我有一个字节数组
unsigned char* array=new unsigned char[4000000];
...
我想得到数组中所有非零元素的索引。
当然,我可以关注
for(int i=0;i<size;i++)
{
if(array[i]!=0) somevector.push_back(i);
}
有比这更快的算法吗?
更新1 我可以看到多数回答是否定的。我希望有一些我不知道的神奇位操作。有些人建议排序,但在这种情况下并不可行。但是非常感谢你的所有答案。
更新2 自此问题发布4年零4个月后,@ wim建议this answer that looks promising。
答案 0 :(得分:4)
除非您的矢量是有序的,否则如果您使用单线程程序,这是执行您想要执行的操作的最有效算法。您可以尝试优化要存储结果的数据结构,但这是您能够做到的最佳时间。
答案 1 :(得分:1)
你唯一能做的就是提高速度。
答案 2 :(得分:1)
如果非零值相对较少,您可以使用的一个技巧是哨兵值:
unsigned char old_value = array[size-1];
array[size-1] = 1; // make sure we find a non-zero eventually
int i=0;
for (;;) {
while (array[i]==0) ++i; // tighter loop
if (i==size-1) break;
somevector.push_back(i);
++i;
}
array[size-1] = old_value;
if (old_value!=0) {
somevector.push_back(size-1);
}
这避免了必须在每次迭代时检查索引和值。
答案 3 :(得分:1)
如果字节数组大部分为零,是一个稀疏数组,则可以通过一次比较4个字节来利用32位CPU。实际比较一次完成4个字节,但是如果任何字节都不为零,那么你必须确定无符号长整数中的哪些字节是非零的,因此需要花费更多精力。如果数组真的很稀疏,那么比较保存的时间可以补偿确定哪些字节非零的额外工作。
最简单的方法是将unsigned char数组的大小设置为4个字节的倍数,这样您就不必担心在循环完成后执行最后几个字节。
我建议对此进行时序研究,因为它纯粹是猜测的,并且有一个点,数组变得非稀疏,这比简单的循环需要更多的时间。
我要问的一个问题是你在做什么,使用数组的非零元素的偏移向量,以及你是否可以取消向量。另一个问题是,如果您需要向量,是否可以在将元素放入数组时构建向量。
unsigned char* array=new unsigned char[4000000];
......
unsigned long *pUlaw = (unsigned long *)array;
for ( ; pUlaw < array + 4000000; pUlaw++) {
if (*pUlaw) {
// at least one byte is non-zero
unsigned char *pUlawByte = (unsigned char *)pUlaw;
if (*pUlawByte)
somevector.push_back(pUlawByte - array);
if (*(pUlawByte+1))
somevector.push_back(pUlawByte - array + 1);
if (*(pUlawByte+2))
somevector.push_back(pUlawByte - array + 2);
if (*(pUlawByte+3))
somevector.push_back(pUlawByte - array + 3);
}
}
答案 4 :(得分:0)
这不是你问题的真正答案,但我试图想象你想要解决的问题。
有时在对矩阵执行运算时(在数学意义上),当您知道绝大多数矩阵元素将为零(稀疏矩阵)时,可以改进运算。您可以通过不使用大数组来进行这样的优化,而只需存储指示非零元素的对{index,value}。