在数组中查找唯一值的最快方法

时间:2013-12-18 20:28:12

标签: arrays matlab

我正在尝试找到一种在数组中查找唯一值的最快方法,并删除0作为唯一值的可能性。

现在我有两个解决方案:

result1 = setxor(0, dataArray(1:end,1)); % This gives the correct solution
result2 = unique(dataArray(1:end,1)); % This solution is faster but doesn't give the same result as result1

dataArray相当于:

dataArray = [0 0; 0 2; 0 4; 0 6; 1 0; 1 2; 1 4; 1 6; 2 0; 2 2; 2 4; 2 6]; % This is a small array, but in my case there are usually over 10 000 lines.

因此,在这种情况下,result1等于[1; 2]result2等于[0; 1; 2]unique功能更快,但我不希望0被考虑。有没有办法使用unique执行此操作,而不将0视为唯一值?还有另一种选择吗?

修改

我想知道各种解决方案。

clc
dataArray = floor(10*rand(10e3,10));
dataArray(mod(dataArray(:,1),3)==0)=0;
% Initial
tic
for ii = 1:10000
   FCT1 = setxor(0, dataArray(:,1));
end
toc
% My solution
tic
for ii = 1:10000
   FCT2 = unique(dataArray(dataArray(:,1)>0,1));
end
toc
% Pursuit solution
tic
for ii = 1:10000
   FCT3 = unique(dataArray(:, 1));
   FCT3(FCT3==0) = [];
end
toc
% Pursuit solution with chappjc comment
tic
for ii = 1:10000
   FCT32 = unique(dataArray(:, 1));
   FCT32 = FCT32(FCT32~=0);
end
toc
% chappjc solution
tic
for ii = 1:10000
   FCT4 = setdiff(unique(dataArray(:,1)),0);
end
toc
% chappjc 2nd solution
tic
for ii = 1:10000
   FCT5 = find(accumarray(dataArray(:,1)+1,1))-1;
   FCT5 = FCT5(FCT5>0);
end
toc

结果:

Elapsed time is 5.153571 seconds. % FCT1 Initial
Elapsed time is 3.837637 seconds. % FCT2 My solution
Elapsed time is 3.464652 seconds. % FCT3 Pursuit solution
Elapsed time is 3.414338 seconds. % FCT32 Pursuit solution with chappjc comment
Elapsed time is 4.097164 seconds. % FCT4 chappjc solution
Elapsed time is 0.936623 seconds. % FCT5 chappjc 2nd solution

但是,sparseaccumarray的解决方案仅适用于integer。这些解决方案不适用于double

4 个答案:

答案 0 :(得分:5)

使用Floris的测试数据演示accumarray这是一个古怪的建议:

a = floor(10*rand(100000, 1)); a(mod(a,3)==0)=0;
result = find(accumarray(nonzeros(a(:,1))+1,1))-1;

感谢Luis Mendo用nonzeros指出,没有必要执行result = result(result>0)

请注意,此解决方案需要整数值数据(不一定是整数数据类型,但不包含十进制组件)。比较浮点值的相等性,如unique所做的那样,是危险的。请参阅herehere


原创建议:将uniquesetdiff结合使用:

result = setdiff(unique(a(:,1)),0)

或者在unique之后使用逻辑索引删除:

result = unique(a(:,1));
result = result(result>0);

我通常不希望将[]指定为(result(result==0)=[];),因为它对大型数据集的效率非常低。

unique之后删除零应该更快,因为它对较少的数据进行操作(除非每个元素都是唯一的,如果a / dataArray非常短,则为OR)。

答案 1 :(得分:5)

只是为了增加一般的喧嚣 - 这里有三种不同的方法。他们都给出了相同的答案,但时间略有不同:

a = floor(10*rand(100000, 1));
a(mod(a,3)==0)=0;
tic
b1 = unique(a(:,1));
b1(b1==0) = [];
toc
tic
b2 = find(sparse(a(:,1)+1, 1, 1)) - 1;
b2(b2==0)=[];
toc
tic
b3 = setxor(0, a(:, 1), 'rows');
toc

display(b1)
display(b2)
display(b3)

在我的机器上,时间(对于100000个元素的数组)如下:

0.0087 s  - for unique
0.0142 s  - for find(sparse)
0.0302 s  = for setxor

对于像这样的问题,我总是喜欢sparse - 您可以将元素的数量与其唯一值同时得到。

根据@chappj的建议

编辑。我添加了第四个选项

b4 = find(accumarray(a(:,1)+1,1)-1);
b4(b4==0) = [];

时间:

0.0029 s , THREE TIMES FASTER THAN UNIQUE

女士们,先生们,我们有一个胜利者。

AFTERWORD 基于索引的方法(sparseaccumarray)仅适用于整数值输入(尽管它们可以是double类型)。根据问题中给出的输入数组,这似乎没问题,但不适用于非整数值输入。当然,当你有双打时,unique是一个棘手的概念 - “看起来”相同的数字可能用不同的方式表示。您可以考虑截断输入数组(清理数据)以确保这不是问题。例如,如果你做了

a = 0.001 * double(int(a * 1000));

您可以将所有值四舍五入到不超过3位有效数字,并且因为您“通过int”,您确定不会得到“非常微妙地不同”的值(例如,在第8位数字中)或更远)。当然,在这种情况下你也可以做

a = round(a * 1000);
mina = min(a(:));
b = find(accumarray(a - mina + 1, 1)) + mina - 1;
b = 0.001 * b(b ~= 0);

对于非整数值,这是“相当强大的”(在上面的例子中,它处理最多三位有效数字的值;如果你需要更多,空间要求最终将变得太大,这个方法将比{更慢] {1}},实际上必须对数据进行排序。)

答案 2 :(得分:3)

为什么不在第二步中删除零:

result2 = unique(.....);
result2 = (result2~=0);

答案 3 :(得分:0)

我还找到了另一种方法:

result2 = unique(dataArray(dataArray(:,1)>0,1));