3

私は 7*1 vector を持っていa = (1:7).'ます。次のように、要素が行列の反対角を形成するように、Aベクトルからサイズ 4*4の行列を形成したいと考えています。aaA

A = [1 2 3 4;
     2 3 4 5;
     3 4 5 6;
     4 5 6 7]

a要素が連続した整数である場合だけでなく、一般的な でこれが機能することを望みます。

助けていただければ幸いです。

4

2 に答える 2

6

インデックス作成の設定

の 2 つの出力を加算するとmeshgrid、次のインデックスが得られます。

[x, y] = meshgrid(1:4, 0:3);
x + y;
% ans = [1     2     3     4
%        2     3     4     5
%        3     4     5     6
%        4     5     6     7];

aあなたの例と同じであれば、そこで止めることができます。または、これを使用して一般的な vector にインデックスを付けますa。比較のために、rahnema1 がメソッドに対して行ったのと同じ入力例を使用します。

a = [4 6 2 7 3 5 1];
[x, y] = meshgrid(1:4, 0:3);
A = a(x + y);    
% A = [4     6     2     7
%      6     2     7     3
%      2     7     3     5
%      7     3     5     1] 

を使用する代わりにインデックスを作成する方法はたくさんmeshgridあります。いくつかの例については、以下のベンチマーク関数を参照してください。


ベンチマークと 7 つの異なる方法。

cumsumrepmathankelおよび単純なforループを使用するメソッドを含む、さまざまなメソッドを実行するタイミングを次に示します。このベンチマークはMatlab 2015bで行われたため、rahnema1の回答のOctaveベンチマークでは実行できないMatlabの最適化などを利用しています。複数回の試行などを行うため、 /timeitよりもロバストな関数も使用します。tictoc

function benchie()
    n = 10000;    % (large) square matrix size
    a = 1:2*n-1;  % array of correct size, could be anything this long
    f1 = @() m1(a,n);  disp(['bsxfun:   ', num2str(timeit(f1))]);
    f2 = @() m2(a,n);  disp(['cumsum:   ', num2str(timeit(f2))]);
    f3 = @() m3(a,n);  disp(['meshgrid: ', num2str(timeit(f3))]);
    f4 = @() m4(a,n);  disp(['repmat:   ', num2str(timeit(f4))]);
    f5 = @() m5(a,n);  disp(['for loop: ', num2str(timeit(f5))]);
    f6 = @() m6(a,n);  disp(['hankel1:  ', num2str(timeit(f6))]); 
    f7 = @() m7(a,n);  disp(['hankel2:  ', num2str(timeit(f7))]); 
end
% Use bsxfun to do broadcasting of addition
function m1(a,n); A = a(bsxfun(@plus, (1:n), (0:n-1).')); end
% Use cumsum to do cumulative vertical addition to create indices
function m2(a,n); A = a(cumsum([(1:n); ones(n-1,n)])); end
% Add the two meshgrid outputs to get indices
function m3(a,n); [x, y] = meshgrid(1:n, 0:n-1); A = a(x + y); end
% Use repmat twice to replicate the meshgrid results, for equivalent one liner
function m4(a,n); A = a(repmat((1:n)',1,n) + repmat(0:n-1,n,1)); end
% Use a simple for loop. Initialise A and assign values to each row in turn
function m5(a,n); A = zeros(n); for ii = 1:n; A(:,ii) = a(ii:ii+n-1); end; end
% Create a Hankel matrix (constant along anti-diagonals) for indexing
function m6(a,n); A = a(hankel(1:n,n:2*n-1)); end
% Create a Hankel matrix directly from elements
function m7(a,n); A = hankel(a(1:n),a(n:2*n-1)); end

出力:

bsxfun:   1.4397 sec
cumsum:   2.0563 sec
meshgrid: 2.0169 sec
repmat:   1.8598 sec
for loop: 0.4953 sec % MUCH quicker!
hankel1:  2.6154 sec
hankel2:  1.4235 sec

したがって、1 つのライナーが必要な場合は、rahnema1 の提案bsxfunまたはマトリックスの直接生成を使用するのが最善です。これは、いくつかの利点hankelを説明する素晴らしい StackOverflow の回答です。Matlab では、いつ bsxfun を使用するのが最適ですか?bsxfun

ただし、for ループは 2 倍以上高速です。結論: Matlab には、このようなことを達成するためのきちんとした方法がたくさんあります。場合によっては、適切な事前割り当てを伴う単純な for ループと、Matlab の内部最適化が最も速い場合があります。

于 2017-06-23T07:11:52.910 に答える
5

使用できますhankel

n= 4;
A= hankel(a(1:n),a(n:2*n-1))

その他の解決策 (expansion/bsxfun):

MATLAB r2016b /Octave では、次のように作成できます。

A = a((1:4)+(0:3).')

r2016b より前では、以下を使用できますbsxfun

A = a(bsxfun(@plus,1:4, (0:3).'))

入出力例

a = [4 6 2 7 3 5 1]

A =

   4   6   2   7
   6   2   7   3
   2   7   3   5
   7   3   5   1

Octave でテストされた @Wolfie が提供するベンチマークを使用します。

 _____________________________________
|Method   |memory peak(MB)|timing(Sec)|
|=========|===============|===========|
|bsxfun   |2030           |1.50       |
|meshgrid |3556           |2.43       |
|repmat   |2411           |2.64       |
|hankel   |886            |0.43       |
|for loop |886            |0.82       |
于 2017-06-23T07:12:07.653 に答える