如何更快地使模数和逻辑索引工作?

时间:2016-08-02 14:20:32

标签: matlab indexing

请考虑以下命令:

c(c>A | c<1) = mod(c(c>A | c<1),A);

其中c是列向量,A是标量。

简而言之:有没有办法让这项工作更快?

说明:

c(i)表示A - by - A矩阵中的列号。但是,它可能具有大于A或小于1的值,并且此命令应将其固定在某种“PAC-MAN”中。办法。如果c(i)大于A,那么当您从{1}开始向后计数时,如果再次从{1}再次开始,则从1开始,依此类推,直到您计算为止到A的值。对于A,这应该以相同的方式工作,因此计数将被颠倒。

示例:

  • 如果c(i)c(i)<1,则在此命令后A = 10 应该是7。

  • 如果c(i) = 17c(i),则在此命令后A = 10 应该是2。

  • 如果c(i) = -8c(i),则在此命令后A = 10 应该是3。

动机:此命令是我所拥有的模拟模型的一部分,目前它是其中最慢的部分。这个特定的行在模型的每个实现中被称为数百万(!)次,并且有很多,所以任何改进都会有所帮助。顺便说一下,c(i) = 213的典型大小约为10K-1。

p.s。:如果您对标题有更好的建议,我会很乐意改变它,我找不到一个好的。

1 个答案:

答案 0 :(得分:4)

您不需要在此处实际执行任何逻辑索引,因为c > A | c < 1不会触及mod排除的任何值,并且通过一切可能会更快mod,而不是进行比较和索引,以确定要传递给mod的值。

c = [17 -8 213, 7];

c = mod(c, A);
%   7   2   3   7

一般而言,对于需要对函数的输入和输出进行逻辑索引的其他函数,您希望将逻辑数组存储在临时变量中而不是计算两次:

touse = c < 1 | c > A;
c(touse) = mod(c(touse), A);

这是一个快速的小基准,显示每种方法的相对表现:

function timemod()

    sizes = round(linspace(100, 100000, 10));

    [times1, times2, times3] = deal(zeros(numel(sizes), 1));

    A = 10;

    for k = 1:numel(sizes)

        data = round(rand(sizes(k), 1) * A * 100);
        times1(k) = timeit(@()indexing(data, A));

        data = round(rand(sizes(k), 1) * A * 100);
        times2(k) = timeit(@()indexing_temp(data, A));

        data = round(rand(sizes(k), 1) * A * 100);
        times3(k) = timeit(@()mod(data, A));
    end

    figure
    plot(sizes, 1000 * cat(2, times1, times2, times3))
    legend({'Indexing', 'Indexing w/ temp', 'No Indexing'})

    xlabel('Number of Elements')
    ylabel('Execution Time (ms)')

    fprintf('Indexing:              %0.2f ms\n', mean(times1 * 1000))
    fprintf('Indexing with temp:    %0.2f ms\n', mean(times2 * 1000))
    fprintf('No Indexing or temp:   %0.2f ms\n', mean(times3 * 1000))
end

function data = indexing(data, A)
    data(data > A | data < 1) = mod(data(data > A | data < 1), A);
end

function data = indexing_temp(data, A)
    inds = data > A | data < 1;
    data(inds) = mod(data(inds), A);
end

enter image description here