基于另一个向量的重复值创建升序向量(MATLAB)

3
这里是一个相对简单的例子。假设我有以下向量('V1'):
1
1
1
2
2
2
2
3
3
4
4
4
4
5
5
5

我想创建第二个向量V2,它以1作为起始值,并且对于V1中的每个值的迭代递增,但在V1的新值处重置。例如:
1
2
3
1
2
3
4
1
2
1
2
3
4
1
2
3

在V1中可能有一个值的迭代,也可能有多达6个值的迭代。

解决方案可能会使用for循环,但我想象中有一种更简单的形式,而不需要使用循环('repmat'就是其中之一)。


我们应该假设 V1 已经排序了吗? - gnovice
如果您可以不使用循环完成它,那就更好了。是的,V1已经排序。 - PyjamaNinja
1
是否存在大于1的间隙(即缺失数字,例如 V1 = [1; 1; 1; 3; 3; 3; 4; 4];)? - gnovice
不,没有任何间隙。这些值按照一个常数(例如1)升序排列。非增量值留待以后解决! - PyjamaNinja
1
好消息!所有下面的解决方案似乎已经解决了非增量值问题。 ;) - gnovice
我本希望点赞数量能够决定哪个答案应该被接受,但它们都差不多! - PyjamaNinja
5个回答

6

不用循环的另一个建议。

首先统计重复值的数量。

a=histc(v1,unique(v1));

构建计数数组
b = ones(1,sum(a));

现在,在适当的位置上对累计总和进行计数:
a = a(1:end-1);
b(cumsum(a)+1) = b(cumsum(a)+1) - a;

最后对累积总和进行求和

cumsum(b)

总共
v1 = [1,1,1,1,2,2,3,3,3,3,3,4];
a=histcounts(v1,[unique(v1),inf]);
b = ones(1,sum(a));
a = a(1:end-1);
b(cumsum(a)+1) = b(cumsum(a)+1) - a;
disp(cumsum(b))

时间测试:

在随机排序的输入V1 = sort(randi(100,1e6,1));上运行,我在Matlab 2017a中得到了以下定时器。

  • Gnovic的第一个建议:2.852872e-02
  • Gnovic的第二个建议:2.909344e-02
  • AVK的建议:3.935982e-01
  • RadioJava的建议:2.441206e-02
  • Nicky的建议:9.153147e-03

参考代码:

function [] = SO()
V1 = sort(randi(100,1e6,1));

t1 = timeit(@() gnovice1(V1)); fprintf("* Gnovic's first suggestion: %d\n",t1);
t2 = timeit(@() gnovice2(V1)); fprintf("* Gnovic's second suggestion: %d\n",t2);
t3 = timeit(@() AVK(V1)); fprintf("* AVK's suggestion: %d\n",t3);
t4 = timeit(@() RadioJava(V1)); fprintf("* RadioJava's suggestion: %d\n",t4);
t5 = timeit(@() Nicky(V1)); fprintf("* Nicky's suggestion: %d\n",t5);


function []=gnovice1(V1)
V2 = accumarray(V1, 1, [], @(x) {1:numel(x)});
V2 = [V2{:}].';

function []=gnovice2(V1)
V2 = ones(size(V1));
V2([find(diff(V1))+1; end]) = 1-accumarray(V1, 1);
V2 = cumsum(V2(1:end-1));

function []=AVK(v)
a= v;
for i=unique(v)'
    a(v==i)= 1:length(a(v==i));
end

function []=RadioJava(vec)
vec = vec(:).';
zero_separated=[1,vec(1:end-1)==vec(2:end)];
c=cumsum(zero_separated);
zeros_ind = ~zero_separated;
d = diff([1 c(zeros_ind)]);
zero_separated(zeros_ind) = -d;
output=cumsum(zero_separated);

function []=Nicky(v1)
v1 = v1(:).';
a=histcounts(v1,[unique(v1),inf]);
b = ones(1,sum(a));
a = a(1:end-1);
b(cumsum(a)+1) = b(cumsum(a)+1) - a;
b = cumsum(b);

5
假设V1已经排序好了,这里有一个使用accumarray向量化解决方案:
V2 = accumarray(V1, 1, [], @(x) {1:numel(x)});
V2 = [V2{:}].';

5

基于此回答中的第二种方法:

t = diff([0; find([diff(V1)~=0; true])]);
V2 = ones(sum(t), 1);
V2(cumsum(t(1:end-1))+1) = 1-t(1:end-1);
V2 = cumsum(V2);

3

不使用循环的解决方案

vec =[1 1 1 2 2 2 3 3 3];    
zero_separated=[1,vec(1:end-1)==vec(2:end)]; % 0 at every new set    
c=cumsum(zero_separated); % Temporary cumsum    
zeros_ind = ~zero_separated;    
d = diff([1 c(zeros_ind)]); % deltas in the temporary cumsum
zero_separated(zeros_ind) = -d; % Set zeros ind to delta    
output=cumsum(zero_separated); % Calculate cumsum now

输出

output = 1     2     3     1     2     3     1     2     3

根据这个问题的解答


类似这样的代码:>> zero_separated(isnan(zero_separated)) = 1-diff([0 find(isnan(zero_separated))]); >> cumsum(zero_separated)...可以运行,但最好的情况是最小数应该为1而不是0。 - PyjamaNinja
1
固定的和更好、更简单的解决方案 - Prostagma

2
v = [1;1;1;2;2;2;2;3;3;4;4;4;4;5;5;5];
a= v;
for i=unique(v)'
    a(v==i)= 1:length(a(v==i));
end
disp(a)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接