【发布时间】:2016-03-11 22:15:17
【问题描述】:
我在 Matlab 中有以下矩阵:
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
每一行只有一个 1。我如何(不循环)确定一个列向量,以便如果第二列中有 1,第一个元素是 2,第二个元素是 3 中的一个第三列等?上面的例子应该变成:
M = [ 3
1
2
1
3];
【问题讨论】:
我在 Matlab 中有以下矩阵:
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
每一行只有一个 1。我如何(不循环)确定一个列向量,以便如果第二列中有 1,第一个元素是 2,第二个元素是 3 中的一个第三列等?上面的例子应该变成:
M = [ 3
1
2
1
3];
【问题讨论】:
您实际上可以通过简单的矩阵乘法来解决这个问题。
result = M * (1:size(M, 2)).';
3
1
2
1
3
这是通过将 M x 3 矩阵与 3 x 1 数组相乘来实现的,其中 3x1 的元素就是 [1; 2; 3]。简而言之,对于M 的每一行,使用 3 x 1 数组执行逐元素乘法。只有M 行中的 1 会在结果中产生任何内容。然后对这个逐元素乘法的结果求和。因为每行只有一个“1”,所以结果将是该 1 所在的列索引。
例如M的第一行。
element_wise_multiplication = [0 0 1] .* [1 2 3]
[0, 0, 3]
sum(element_wise_multiplication)
3
更新
基于下面@reyryeng和@Luis提供的解决方案,我决定进行比较,看看各种方法的性能比较。
为了设置测试矩阵 (M),我创建了原始问题中指定形式的矩阵并改变了行数。使用randi([1 nCols], size(M, 1)) 随机选择哪一列有 1。使用timeit分析执行时间。
当使用 double 类型的 M(MATLAB 的默认设置)运行时,您将获得以下执行时间。
如果M 是logical,则矩阵乘法会受到影响,因为它必须在矩阵乘法之前转换为数值类型,而其他两个有一点性能改进.
这是我使用的测试代码。
sizes = round(linspace(100, 100000, 100));
times = zeros(numel(sizes), 3);
for k = 1:numel(sizes)
M = generateM(sizes(k));
times(k,1) = timeit(@()M * (1:size(M, 2)).');
M = generateM(sizes(k));
times(k,2) = timeit(@()max(M, [], 2), 2);
M = generateM(sizes(k));
times(k,3) = timeit(@()find(M.'), 2);
end
figure
plot(range, times / 1000);
legend({'Multiplication', 'Max', 'Find'})
xlabel('Number of rows in M')
ylabel('Execution Time (ms)')
function M = generateM(nRows)
M = zeros(nRows, 3);
col = randi([1 size(M, 2)], 1, size(M, 1));
M(sub2ind(size(M), 1:numel(col), col)) = 1;
end
【讨论】:
你也可以滥用find,观察M转置的行位置。您必须先转置矩阵,因为find 按列主要顺序运行:
M = [0 0 1
1 0 0
0 1 0
1 0 0
0 0 1];
[out,~] = find(M.');
不确定这是否比矩阵乘法更快。
【讨论】:
find 是[out,~] = ... 与out = ... 不同的罕见情况之一
【讨论】:
max。我想知道所有方法之间的性能比较是什么。我的钱在max
2?