Добавить диагональ нулей в матрицу в MATLAB
Предположим, у меня есть матрица A
измерения Nx(N-1)
в MATLAB, например,
N=5;
A=[1 2 3 4;
5 6 7 8;
9 10 11 12;
13 14 15 16;
17 18 19 20 ];
Я хочу, чтобы преобразовать A
на NxN
матрица B
, просто добавив нулевую диагональ, т. е.
B=[ 0 1 2 3 4;
5 0 6 7 8;
9 10 0 11 12;
13 14 15 0 16;
17 18 19 20 0];
этот код делает то, что я хочу:
B_temp = zeros(N,N);
B_temp(1,:) = [0 A(1,:)];
B_temp(N,:) = [A(N,:) 0];
for j=2:N-1
B_temp(j,:)= [A(j,1:j-1) 0 A(j,j:end)];
end
B = B_temp;
не могли бы вы предложить эффективный способ векторизации?
3 ответов
вы можете сделать это с верхней и нижней треугольной части Матрицы (triu
и tril
).
тогда это 1 решения строку:
B = [tril(A,-1) zeros(N, 1)] + [zeros(N,1) triu(A)];
Edit: benchmark
это сравнение метода цикла, 2 метода в Сардара, и мой метод.
контрольный код, используя timeit
для времени и сразу поднимаясь кода от вопроса и ответы:
function benchie()
N = 1e4; A = rand(N,N-1); % Initialise large matrix
% Set up anonymous functions for input to timeit
s1 = @() sardar1(A,N); s2 = @() sardar2(A,N);
w = @() wolfie(A,N); u = @() user3285148(A,N);
% timings
timeit(s1), timeit(s2), timeit(w), timeit(u)
end
function sardar1(A, N) % using eye as an indexing matrix
B=double(~eye(N)); B(find(B))=A.'; B=B.';
end
function sardar2(A,N) % similar to sardar1, but avoiding slow operations
B=1-eye(N); B(logical(B))=A.'; B=B.';
end
function wolfie(A,N) % using triangular parts of the matrix
B = [tril(A,-1) zeros(N, 1)] + [zeros(N,1) triu(A)];
end
function user3285148(A, N) % original looping method
B = zeros(N,N); B(1,:) = [0 A(1,:)]; B(N,:) = [A(N,:) 0];
for j=2:N-1; B(j,:)= [A(j,1:j-1) 0 A(j,j:end)]; end
end
результаты:
- Сардар метод 1: 2.83 сек
- Сардар Метод 2: 1.82 сек
- мой метод: 1.45 сек
- метод зацикливания: 3,80 сек (!)
выводы:
- ваше желание векторизовать это было хорошо обосновано, цикл намного медленнее, чем другие методы.
- избегая преобразования данных и
find
для больших матриц важно, экономия ~35% время обработки между методами Сардара. - избегая индексирования все вместе, вы можете сохранить дальше время обработки 20%.
создайте матрицу с нулями по диагонали и единицами по недиагональным индексам. Замените недиагональные элементы транспонированием A
(поскольку MATLAB является основным столбцом). Транспонируйте снова, чтобы получить правильный порядок.
B = double(~eye(N)); %Converting to double since we want to replace with double entries
B(find(B)) = A.'; %Replacing the entries
B = B.'; %Transposing again to get the matrix in the correct order
Edit:
As предложил by Вулфи для того же алгоритма вы можете избавиться от преобразования в double
и использование find
с:
B = 1-eye(N);
B(logical(B)) = A.';
B = B.';
если вы хотите вставить любой вектор по диагонали матрицы, можно использовать простое индексирование. Следующий фрагмент дает вам индексы нужной диагонали, учитывая размер квадратной матрицы n
(матрица n
by n
), и номер диагонали k
, где k=0
соответствует главной диагонали положительные числа k
к верхним диагоналям и отрицательным числам k
в нижней диагонали. ixd
наконец дает вам 2D индексы.
function [idx] = diagidx(n,k)
% n size of square matrix
% k number of diagonal
if k==0 % identity
idx = [(1:n).' (1:n).']; % [row col]
elseif k>0 % Upper diagonal
idx = [(1:n-k).' (1+k:n).'];
elseif k<0 % lower diagonal
idx = [(1+abs(k):n).' (1:n-abs(k)).'];
end
end
использование:
n=10;
k=3;
A = rand(n);
idx = diagidx(n,k);
A(idx) = 1:(n-k);