【问题标题】:Create vector which ascends based on repeated values of another vector (MATLAB)创建基于另一个向量的重复值上升的向量(MATLAB)
【发布时间】:2018-04-11 16:37:33
【问题描述】:

这是一个相对简单的。假设我有以下向量('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 已排序?
  • 如果你可以在没有循环的情况下做到这一点,那就更好了。是的,V1 已排序。
  • 是否存在大于 1 的间隙(即缺少数字,例如 V1 = [1; 1; 1; 3; 3; 3; 4; 4];)?
  • 不,没有任何差距。这些值增加一个常数(例如,1)。非增量值是另一天的问题!
  • 好吧,好消息!以下所有解决方案似乎已经解决了非增量值问题。 ;)

标签: matlab vector repeat


【解决方案1】:

另一个没有循环的建议。

先统计重复值的个数

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))

TIMEIT:

在随机排序的输入 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);

【讨论】:

    【解决方案2】:

    假设V1 已排序,这是使用accumarray 的矢量化解决方案:

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

    【讨论】:

      【解决方案3】:

      基于this answer中的第二种方法:

      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);
      

      【讨论】:

        【解决方案4】:

        没有循环的解决方案

        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
        

        基于this

        【讨论】:

        • 类似:>> zero_separated(isnan(zero_separated)) = 1-diff([0 find(isnan(zero_separated))]); >> cumsum(zero_separated)...可行,但理想情况下,最小的数字是 1 而不是 0
        • 固定和更好更简单的解决方案
        【解决方案5】:
        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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-05-29
          • 2011-04-23
          • 1970-01-01
          • 2020-08-08
          • 1970-01-01
          • 2017-09-10
          • 2020-01-19
          相关资源
          最近更新 更多