我希望通过将向量n次串联来创建矩阵。所以如果我的向量是mx1
,那么我的矩阵就是mxn
,并且矩阵的每一列都等于该向量。
以下哪种方法最好/正确,或者也许有更好的方法我不知道?
matrix = repmat(vector, 1, n);
matrix = vector * ones(1, n);
谢谢
timeit
进行一些基准测试,涉及不同的向量大小和重复因子。所展示的结果是在Windows上使用Matlab R2015b进行的。%// repmat approach
function matrix = f_repmat(vector, n)
matrix = repmat(vector, 1, n);
%// multiply approach
function matrix = f_multiply(vector, n)
matrix = vector * ones(1, n);
%// indexing approach
function matrix = f_indexing(vector,n)
matrix = vector(:,ones(1,n));
然后生成不同大小的向量,并使用不同的重复因子:
M = round(logspace(2,4,15)); %// vector sizes
N = round(logspace(2,3,15)); %// repetition factors
time_repmat = NaN(numel(M), numel(N)); %// preallocate results
time_multiply = NaN(numel(M), numel(N));
time_indexing = NaN(numel(M), numel(N));
for ind_m = 1:numel(M);
for ind_n = 1:numel(N);
vector = (1:M(ind_m)).';
n = N(ind_n);
time_repmat(ind_m, ind_n) = timeit(@() f_repmat(vector, n)); %// measure time
time_multiply(ind_m, ind_n) = timeit(@() f_multiply(vector, n));
time_indexing(ind_m, ind_n) = timeit(@() f_indexing(vector, n));
end
end
以下是使用 repmat
作为参考绘制的结果,如下两张图所示:
figure
imagesc(time_multiply./time_repmat)
set(gca, 'xtick',1:2:numel(N), 'xticklabels',N(1:2:end))
set(gca, 'ytick',1:2:numel(M), 'yticklabels',M(1:2:end))
title('Time of multiply / time of repmat')
axis image
colorbar
figure
imagesc(time_indexing./time_repmat)
set(gca, 'xtick',1:2:numel(N), 'xticklabels',N(1:2:end))
set(gca, 'ytick',1:2:numel(M), 'yticklabels',M(1:2:end))
title('Time of indexing / time of repmat')
axis image
colorbar
也许更好的比较方式是,针对每个测试向量大小和重复因子,指出哪种方法最快:figure
times = cat(3, time_repmat, time_multiply, time_indexing);
[~, fastest] = min(times, [], 3);
imagesc(fastest)
set(gca, 'xtick',1:2:numel(N), 'xticklabels',N(1:2:end))
set(gca, 'ytick',1:2:numel(M), 'yticklabels',M(1:2:end))
title('1: repmat is fastest; 2: multiply is; 3: indexing is')
axis image
colorbar
从这些数据可以得出一些结论:
repmat
慢repmat
类似。对于向量大小或重复因子较大的情况,它往往更快,而对于较小的情况则更慢。repmat
是一个更通用的多维矩阵复制解决方案,因此速度较慢。将两个向量相乘的特定“自制”解决方案可能更快。甚至可以使用选择而不是乘法,例如vector(:,ones(n,1))
而不是 vector*ones(1,n)
。open repmat
。如您所见,它不是一个内置函数。您可以看到它还使用了 ones
(选择)来复制矩阵。然而,由于它是一个更通用的解决方案(适用于标量和多维矩阵以及在多个方向上进行复制),您将发现不必要的 if
语句和其他不必要的代码,从而有效地降低了速度。ones
乘以向量会变得更慢。毫无疑问的优胜者是使用选择的 ones
,即vector(:,ones(n,1))
(由于它使用相同的策略,应始终比 repmat
更快)。如果两种方法都能提供您所需的输出结果,那么两种方法均正确。
但是,根据您如何声明向量,您可能会得到使用repmat
时会出现错误结果的情况,如果您使用ones
,则这些错误将被发现。例如,看下面这个例子。
>> v = 1:10;
>> m = v * ones(1, n)
Error using *
Inner matrix dimensions must agree.
>> m = repmat(v, 1, n)
m =
Columns 1 through 22
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2
Columns 23 through 44
3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 1 2 3 4
Columns 45 through 50
5 6 7 8 9 10
ones
提供了错误提示,让您知道您没有做正确的事情,但是 repmat
没有提供。虽然这个例子在使用 repmat
和 ones
都能正常工作。
>> v = (1:10).';
>> m = v * ones(1, n)
m =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
6 6 6 6 6
7 7 7 7 7
8 8 8 8 8
9 9 9 9 9
10 10 10 10 10
>> m = repmat(v, 1, n)
m =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
6 6 6 6 6
7 7 7 7 7
8 8 8 8 8
9 9 9 9 9
10 10 10 10 10
vector(:,ones(1,n))
但是,如果我必须选择,repmat
对我来说是首选方法,因为它正是为此目的而制作的。另外,根据您将如何使用此复制的数组,您可以使用bsxfun
避免完全创建它,在其输入数组上执行即时复制和一些操作以应用于输入。这里有一个比较 - Comparing BSXFUN and REPMAT
,显示在大多数情况下bsxfun
比repmat
更好。
出于性能考虑,让我们测试一下这些内容。这里有一个基准测试代码 -
%// Inputs
vector = rand(1000,1);
n = 1000;
%// Warm up tic/toc.
for iter = 1:50000
tic(); elapsed = toc();
end
disp(' ------- With REPMAT -------')
tic,
for iter = 1:200
A = repmat(vector, 1, n);
end
toc, clear A
disp(' ------- With vector(:,ones(1,n)) -------')
tic,
for iter = 1:200
A = vector(:,ones(1,n));
end
toc, clear A
disp(' ------- With vector * ones(1, n) -------')
tic,
for iter = 1:200
A = vector * ones(1, n);
end
toc
运行时结果 -
------- With REPMAT -------
Elapsed time is 1.241546 seconds.
------- With vector(:,ones(1,n)) -------
Elapsed time is 1.212566 seconds.
------- With vector * ones(1, n) -------
Elapsed time is 3.023552 seconds.
matrix = bsxfun(@times , vector , ones(1,n) )
这个怎么样?它可能会更快。 - Hoki