2

我有一个名为 l 的矩阵,大小为 20X3。我想做的是:假设我有这个限制:

l1_max=20; l1_min=0.5;
l2_max=20; l2_min=0.5;
mu_max=20; mu_min=0.5;

我想强制矩阵 l 的所有元素在限制范围内。l1_max 和 l1_min 中第一列的值。l2_max 和 l2_min 内第二列的值。mu_max 和 mu_min 中的第 3 列的值。

我所做的是这样的:

for k=1:20
    if l(k,1)>l1_max 
        l(k,1) = l1_max;
    elseif l(k,1)<l1_min
        l(k,1) = l1_min;
    end

    if l(k,2)>l2_max 
        l(k,2) = l2_max;
    elseif l(k,2)<l2_min
        l(k,2) = l2_min;
    end

    if l(k,3)>mu_max 
        l(k,3) = mu_max;
    elseif l(k,3)<mu_min
        l(k,3) = mu_min;
    end
end

可以以更好的方式完成吗?

4

3 回答 3

4

您不必遍历行,对整列使用矢量化操作:

l(l(:, 1) > l1_max, 1) = l1_max;
l(l(:, 1) < l1_min, 1) = l1_min;

同样:

l(l(:, 2) > l2_max, 2) = l2_max;
l(l(:, 2) < l2_min, 2) = l2_min;
l(l(:, 3) > l2_max, 3) = mu_max;
l(l(:, 3) < l2_min, 3) = mu_min;

类似于 Bas 的想法的另一种方法是应用minmax如下所示:

l(:, 1) = max(min(l(:, 1), l1_max), l1_min);
l(:, 2) = max(min(l(:, 2), l2_max), l2_min);
l(:, 3) = max(min(l(:, 3), mu_max), mu_min);

看来这两种方法具有可比的性能。

于 2013-08-13T06:11:24.133 回答
2

您甚至不必遍历所有列,对整个矩阵的操作可以在 2 次调用中完成bsxfun,与列数无关:

column_max = [l1_max, l2_max, mu_max];
column_min = [l1_min, l2_min, mu_min];

M = bsxfun(@min, M, column_max); %clip to maximum
M = bsxfun(@max, M, column_min); %clip to minimum

这使用了两个技巧:在 min_val 和 max_val 之间裁剪clipped_x = min(max(x, min_val), max_val)一个值,你可以做. 另一个技巧是使用有点晦涩的bsxfun方法,它在进行单例扩展后应用一个函数。当您在两个矩阵上使用它时,它会在应用函数之前将最小的矩阵“挤出”到与最大矩阵相同的大小,因此上面的示例等效于 M = min(M, repmat(column_max, size(M, 1), 1)),但希望以更有效的方式计算。

于 2013-08-13T11:59:44.547 回答
1

以下是测试目前讨论的各种方法的基准。我正在使用 File Exchange 上的TIMEIT功能。

function [t,v] = testClampColumns()
    % data and limits ranges for each column
    r = 10000; c = 500;
    M = randn(r,c);
    mn = -1.1 * ones(1,c);
    mx = +1.1 * ones(1,c);

    % functions
    f = { ...
        @() clamp1(M,mn,mx) ;
        @() clamp2(M,mn,mx) ;
        @() clamp3(M,mn,mx) ;
        @() clamp4(M,mn,mx) ;
        @() clamp5(M,mn,mx) ;
    };

    % timeit and check results
    t = cellfun(@timeit, f, 'UniformOutput',true);
    v = cellfun(@feval, f, 'UniformOutput',false);
    assert(isequal(v{:}))
end

给定以下实现:

1)循环所有值并与最小值/最大值进行比较

function M = clamp1(M, mn, mx)
    for j=1:size(M,2)
        for i=1:size(M,1)
            if M(i,j) > mx(j)
                M(i,j) = mx(j);
            elseif M(i,j) < mn(j)
                M(i,j) = mn(j);
            end
        end
    end
end

2)将每列与最小值/最大值进行比较

function M = clamp2(M, mn, mx)
    for j=1:size(M,2)
        M(M(:,j) < mn(j), j) = mn(j);
        M(M(:,j) > mx(j), j) = mx(j);
    end
end

3)将每列截断到限制

function M = clamp3(M, mn, mx)
    for j=1:size(M,2)
        M(:,j) = min(max(M(:,j), mn(j)), mx(j));
    end
end

4) (3) 中截断的向量化版本

function M = clamp4(M, mn, mx)
    M = bsxfun(@min, bsxfun(@max, M, mn), mx);
end

5)绝对值比较:-a < x < a <==> |x| <一个

(注意:这不适用于您的情况,因为它需要一个对称的限制范围。我只是为了完整性而将其包括在内。此外,它原来是最慢的方法。)

function M = clamp5(M, mn, mx)
    assert(isequal(-mn,mx), 'Only works when -mn==mx')
    idx = bsxfun(@gt, abs(M), mx);
    v = bsxfun(@times, sign(M), mx);
    M(idx) = v(idx);
end

我在机器上使用大小为 10000x500 的输入矩阵的时间:

>> t = testClampColumns
t =
    0.2424
    0.1267
    0.0569
    0.0409
    0.2868

我会说所有上述方法都足够快,bsxfun解决方案是最快的:)

于 2013-08-18T08:34:13.037 回答