【问题标题】:Efficient colon operator for multiple start and end points用于多个起点和终点的高效冒号运算符
【发布时间】:2016-11-24 20:42:41
【问题描述】:

假设我有以下两个变量:

start_idx = [1 4 7];
end_idx   = [2 6 15];

我想有效地(如果可能,不要使用 for 循环)生成一行,其中包含在 start_idxend_idx 的相应元素之间应用的冒号运算符。对于此示例,这将导致:

result = [1:2 4:6 7:15];

因此:

results = [1 2 4 5 6 7 8 9 10 11 12 13 14 15];

执行此操作的方法应该可以在 Simulink 的 MATLAB Function 模块中使用。非常感谢!

【问题讨论】:

  • out=cell2mat(arrayfun(@(x,y)[x:y],start_idx,end_idx,'uniformoutput',false));
  • arrayfun 比 for 循环快吗?
  • 并非如此。你的sart_idxend_idx 向量的大小是多少?我相信它们必须相当大,才能让这段代码对你的代码速度产生不可忽视的影响
  • 您对其中一个答案的回复说您需要它来生成代码 - 您是指使用 Simulink Coder 吗?这将是包含在问题中的有用信息。但鉴于此操作的结果将是一个可变长度信号,您还必须设置 MATLAB Fcn 块以正确处理它。最后,恕我直言,在循环中执行此操作比寻找一些高度混淆的单列更清晰、更易于理解。

标签: matlab vectorization simulink


【解决方案1】:

这是一种基于累积求和的矢量化方法 -

% Get lengths of each group
lens = end_idx - start_idx + 1;

% Determine positions in o/p array where groups would shift
shift_idx = cumsum(lens(1:end-1))+1

% Initialize ID array and at shifting positions place strategically created
% numbers, such that when ID array is cumulatively summed would result in
% desired "ramped" array
id_arr = ones(1,sum(lens));
id_arr([1 shift_idx]) = [start_idx(1) start_idx(2:end) - end_idx(1:end-1)];
out = cumsum(id_arr)

示例运行 -

start_idx =
     6     8    13
end_idx =
    11    11    15
out =
     6     7     8     9    10    11     8     9    10    11    13    14    15

【讨论】:

    【解决方案2】:

    如果你测量这段代码的运行时间,在清理工作空间后,你会看到平均需要 0.004 秒,而 Divakar 的代码也需要大致相同的时间,即 0.007 秒..

    start_idx=[2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44];
    end_idx=[100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 ...
    1400 1500 1600 1700 1800 1900 2000 2100 2200];
    tic
    final_arr=[];
    for i=1:length(start_idx)
    final_arr=[final_arr,start_idx(i):end_idx(i)];
    end
    toc
    final_arr
    

    如您所见,我使用了长度较长的起始和结束 idx 数组,并确保结束数组元素与它们各自的起始数组元素相距很远。

    命令“toc”之后的经过时间总是根据 CPU 上的负载而变化。当我测量时间时,除了 MATLAB,我只打开了 1-2 个应用程序,并在执行此代码之前清除了工作区. final_arr 有大约 24k 个元素,但处理输出所花费的时间并不多。

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      这很麻烦,但也许更快:

      x = min(start_idx):max(end_idx);
      m = sum(bsxfun(@ge,x,start_idx(:)),1)==numel(end_idx)-sum(bsxfun(@le,x,end_idx(:)),1)+1;
      result = x(m);
      

      它正确处理空范围,即

      start_idx = [1 4 16]
      end_idx   = [2 6 15];
      

      给予

      result =
           1     2     4     5     6
      

      【讨论】:

        【解决方案4】:

        正如我在 cmets 中指出的那样,解决这个问题的唯一方法是:

        out=cell2mat(arrayfun(@(x,y)[x:y],start_idx,end_idx,'uniformoutput',false));
        

        arrayfun 调用将创建一个元胞数组,其中每个元胞都是输出的一部分:

        ans =
        
             1     2
        
        
        ans =
        
             4     5     6
        
        
        ans =
        
          Columns 1 through 8
        
             7     8     9    10    11    12    13    14
        
          Column 9
        
            15
        

        通过将其包装在 cell2mat 调用中,您将获得预期的输出:

        out =
        
          Columns 1 through 8
        
             1     2     4     5     6     7     8     9
        
          Columns 9 through 14
        
            10    11    12    13    14    15
        

        【讨论】:

        • 代码生成不支持匿名函数,所以我不能在 Simulink 的 MATLAB Function 块中使用它——这是要求。感谢您的意见,但您能想到其他方法吗?
        • 必须支持代码生成。代码生成不支持 coder.extrinsic。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-07
        • 2018-01-02
        • 1970-01-01
        • 2016-10-12
        相关资源
        最近更新 更多