【问题标题】:Vectorizable FIND function with if statement MATLAB带有 if 语句的向量化 FIND 函数 MATLAB
【发布时间】:2019-05-29 15:10:12
【问题描述】:

我有一个矩阵u,我想遍历所有行和所有列并执行以下操作。如果元素不为零,则返回行索引的值。如果元素为零,则查找该元素之后的下一个非零元素的行索引。我可以使用两个带有查找功能的 for 循环轻松地做到这一点。但是我需要多次执行此操作(不是因为矩阵的大小,而是因为这被多次调用)。我怎样才能更快地做到这一点?

这是for循环代码:

     for w=scenario_size:-1:1
            for t=1:time_size
                l = u(t,w) ;
                if l~=0
                    tprime = t ;
                else
                    tprime = t+ find(u(t:end,w),1,'first') -1 ;
                end
                i(t,w)       = tprime ;
                boo(t,w)     = number(tprime,w)/u(tprime,w) ;
            end
end

例如,如果一列是[0,0,5,1,0,3],则i[3,3,3,4,6,6]u 的任何一列的最后一个元素总是非零(我通过在末尾人为地添加一行 1 来强制这样做)。

那么boo 是某个矩阵numbertprime 的对应条目除以对应的u(在构造上不为零)。

【问题讨论】:

  • 请定义输入(scenario_size 等)以便代码可以运行,并指出所需的输出是什么
  • 另外,提供一个小例子及其预期输出。我是否理解如果一列是[0,0,5,1,0,3],那么它的预期输出是[3,3,3,4,6,6]
  • @LuisMendo 您可以接受任何输入 - 问题很笼统。说scenario_size = time_size = 3。随便取一个u
  • 当然这个问题很笼统。但是,如果您不提供示例输入和输出,我们无法确定 (1) 我们在运行代码时考虑了您可能想到的输入限制,以及 (2) 我们正在生成您想要的确切输出。例如(但请解决我上面的一般观点),您说如果元素为零,则查找该元素之后的下一个非零元素的行索引。如果没有这样的元素怎么办?在问题变得更清楚之前,我投票决定以不明确的方式结束
  • @Florian 你理解正确。还需要一个额外的步骤。我正在用你的例子更新问题。

标签: matlab for-loop


【解决方案1】:

您可以使用findcummin 和一些logical indexing 来解决此问题。从这个例子开始:

>> u = randi([0 1], 10);
>> u(end, :) = 1

u =

     0     0     0     0     1     0     1     0     1     1
     1     0     1     1     1     0     0     0     0     1
     0     0     0     0     0     1     0     1     1     0
     1     1     1     0     1     1     0     0     0     0
     0     0     1     0     0     0     0     0     1     0
     1     1     1     0     0     0     0     0     0     0
     0     1     0     1     0     0     1     1     0     1
     1     1     0     1     0     1     1     1     1     1
     1     0     0     1     0     0     1     1     1     1
     1     1     1     1     1     1     1     1     1     1

以下将做你想做的事:

i = nan(size(u));  % Start with all nan values
[r, ~] = find(u);  % Get row indices of non-zero values
i(u ~= 0) = r;     % Place row indices in locations of non-zero values
i = cummin(i, 1, 'reverse');  % Column-wise cumulative minimum, starting from bottom

结果:

i =

     2     4     2     2     1     3     1     3     1     1
     2     4     2     2     2     3     7     3     3     2
     4     4     4     7     4     3     7     3     3     7
     4     4     4     7     4     4     7     7     5     7
     6     6     5     7    10     8     7     7     5     7
     6     6     6     7    10     8     7     7     8     7
     8     7    10     7    10     8     7     7     8     7
     8     8    10     8    10     8     8     8     8     8
     9    10    10     9    10    10     9     9     9     9
    10    10    10    10    10    10    10    10    10    10

然后您可以通过将i 转换为linear index 来计算您的矩阵boo

index = i+time_size.*repmat(0:(scenario_size-1), time_size, 1);  % Create linear index
boo = number(index)./u(index);

或者,您可以从一开始就将i 计算为线性索引:

i = nan(size(u));  % Start with all nan values
index = find(u);   % Get linear indices of non-zero values
i(index) = index;  % Place linear indices in locations of non-zero values
i = cummin(i, 1, 'reverse');  % Column-wise cumulative minimum, starting from bottom
boo = number(i)./u(i);

【讨论】:

  • 这会比我的更快吗?我们都使用查找功能
  • @bissi:以上不需要循环。这是对find 的一次调用,而不是循环中的多次调用。我希望它应该更快,但您必须对其进行测试才能确定。
  • 我确认这适用于计算 i。您将如何替换循环的最后一行:boo(t,w) = number(tprime,w)/u(tprime,w) ;?我还没有查看时间
  • @bissi:我已经在当前答案中添加了如何计算boo
  • 这是一个比我的数据上多次调用查找更快的命令。
【解决方案2】:

@gnovice 答案很好,只是为了给find 函数提供一个替代方案

  • 首先是u 矩阵
    >> u = randi([0 1], 10);
    u(end, :) = 1;
    >> u

    u =

         0     0     0     0     0     1     1     0     0     0
         0     1     1     1     0     1     0     1     1     1
         1     1     1     0     1     0     0     1     1     0
         1     1     1     1     1     0     1     1     0     1
         1     1     0     1     1     1     1     0     0     0
         0     0     1     0     1     1     1     0     0     1
         0     1     0     0     1     1     0     0     0     1
         0     1     0     1     1     1     0     1     1     0
         0     0     0     1     0     1     1     1     1     1
         1     1     1     1     1     1     1     1     1     1
  • 然后non-zeros 行索引可以按如下方式计算
    >> t = 1:10*10;% All elements indices
    r = t(u ~= 0); % All non-zeros elements indices 
    >> r

    r =

      Columns 1 through 18

         3     4     5    10    12    13    14    15    17    18    20    22    23    24    26    30    32    34

      Columns 19 through 36

        35    38    39    40    43    44    45    46    47    48    50    51    52    55    56    57    58    59

      Columns 37 through 54

        60    61    64    65    66    69    70    72    73    74    78    79    80    82    83    88    89    90

      Columns 55 through 60

        92    94    96    97    99   100

【讨论】:

  • 之前的回答很快——我没有测试过你的解决方案。
  • 我会使用t = 1:numel(u),只是为了避免代码中出现幻数。这可能比find 更快。
  • @CrisLuengo 你说得对,只是想让 OP 看到我使用u 的大小,他似乎对使用内置函数不感兴趣
  • 一小点:r 将包含 linear 索引,而不是 row 索引。对于行索引,您可以将t 更改为t = repmat(1:size(u, 1), 1, size(u, 2));
  • @Adam 你能把你的逻辑扩展到 3D 数组吗? stackoverflow.com/questions/60799304/…
猜你喜欢
  • 1970-01-01
  • 2012-10-29
  • 1970-01-01
  • 2020-04-10
  • 1970-01-01
  • 2014-08-30
  • 2017-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多