Matlab:创建行向量相同的矩阵。使用repmat()或乘以ones()函数。

3

我希望通过将向量n次串联来创建矩阵。所以如果我的向量是mx1,那么我的矩阵就是mxn,并且矩阵的每一列都等于该向量。

以下哪种方法最好/正确,或者也许有更好的方法我不知道?

matrix = repmat(vector, 1, n);
matrix = vector * ones(1, n);

谢谢


1
matrix = bsxfun(@times , vector , ones(1,n) ) 这个怎么样?它可能会更快。 - Hoki
4个回答

4
这里使用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

enter image description here enter image description here

也许更好的比较方式是,针对每个测试向量大小和重复因子,指出哪种方法最快
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

enter image description here

从这些数据可以得出一些结论

  • 基于乘法的方法总是比repmat
  • 基于索引的方法与repmat类似。对于向量大小或重复因子较大的情况,它往往更快,而对于较小的情况则更慢。

我想我搞定了前两个图,但第三个...无法解释。我猜我习惯了那些上下攀爬的线图 :) - Divakar
@Divakar 第三个告诉我们哪种方法最快。它只包含每个(大小,重复因子)-bin的1、2或3。 - Luis Mendo

2
两种方法都是正确的,但是 repmat 是一个更通用的多维矩阵复制解决方案,因此速度较慢。将两个向量相乘的特定“自制”解决方案可能更快。甚至可以使用选择而不是乘法,例如vector(:,ones(n,1)) 而不是 vector*ones(1,n)
编辑: 在 Command Window 中输入 open repmat。如您所见,它不是一个内置函数。您可以看到它还使用了 ones(选择)来复制矩阵。然而,由于它是一个更通用的解决方案(适用于标量和多维矩阵以及在多个方向上进行复制),您将发现不必要的 if 语句和其他不必要的代码,从而有效地降低了速度。
编辑: 对于非常大的向量,使用 ones 乘以向量会变得更慢。毫无疑问的优胜者是使用选择的 ones,即vector(:,ones(n,1)) (由于它使用相同的策略,应始终比 repmat 更快)。

2

如果两种方法都能提供您所需的输出结果,那么两种方法均正确。

但是,根据您如何声明向量,您可能会得到使用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 没有提供。虽然这个例子在使用 repmatones 都能正常工作。

>> 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

2
你也可以这样做 -
vector(:,ones(1,n))

但是,如果我必须选择,repmat对我来说是首选方法,因为它正是为此目的而制作的。另外,根据您将如何使用此复制的数组,您可以使用bsxfun避免完全创建它,在其输入数组上执行即时复制和一些操作以应用于输入。这里有一个比较 - Comparing BSXFUN and REPMAT,显示在大多数情况下bsxfunrepmat更好。

基准测试

出于性能考虑,让我们测试一下这些内容。这里有一个基准测试代码 -

%// 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.

我也在进行一些时间测试。我很快会发布它们。 - Luis Mendo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接