Создание неквадратной диагональной матрицы из вектора

У меня есть вектор, и я хотел бы, чтобы он повторялся n-раз вперед и n-назад, но по диагонали.

Например, у меня есть вектор:
x= [0 0 1 1 0 0],

и хотел бы матрицу размером 6x5 следующим образом:

1 0 0 0 0
1 1 0 0 0
0 1 1 0 0
0 0 1 1 0
0 0 0 1 1
0 0 0 0 1

Это означает, что вектор [0 0 1 1 0 0] (транспонированный) находится посередине, и мне бы хотелось, чтобы матрица была такова, чтобы при перемещении влево элементы перемещались по кругу вверх при каждом сдвиге влево. Аналогично, при перемещении вправо элементы смещаются по кругу вниз к нижней части для каждого сдвига вправо. Поэтому второй столбец будет [0 1 1 0 0 0] где элементы смещены влево по кругу один раз, тогда первый столбец будет [1 1 0 0 0 0] где мы сдвигаем все элементы влево дважды по отношению к середине и один раз по отношению ко второму столбцу.

Точно так же четвертый столбец будет [0 0 0 1 1 0] Это означает, что по отношению к среднему столбцу мы один раз сместим все элементы по кругу вправо, тогда последний столбец будет [0 0 0 0 1 1] где мы сдвигаем все элементы вправо дважды по отношению к середине и один раз по отношению к четвертому столбцу.

2 ответа

Решение

Если вектор всегда состоит из ненулевой части в середине, вы можете использовать convmtx (из панели инструментов обработки сигналов) следующим образом:

y = convmtx(nonzeros(x), numel(x)-1);

Или, если у вас нет набора инструментов для обработки сигналов, используйте conv2:

y = conv2(eye(numel(x)-1), nonzeros(x)):

За x = [0 0 1 1 0 0] любой из вышеперечисленных производит:

y =
     1     0     0     0     0
     1     1     0     0     0
     0     1     1     0     0
     0     0     1     1     0
     0     0     0     1     1
     0     0     0     0     1

convmtx требуется инструмент обработки сигналов для вычисления результата. Хотя ответ Луиса очень хороший, могу ли я предложить метод, не основанный на наборе инструментов?

n = 2;
ind = mod(bsxfun(@plus, (0:numel(x)-1).', n:-1:-n), numel(x)) + 1;
y = x(ind);

Или, если вы не хотите промежуточную переменную:

n = 2;
y = x(mod(bsxfun(@plus, (0:numel(x)-1).', n:-1:-n), numel(x)) + 1);

За x = [0 0 1 1 0 0];, мы получаем:

y =

     1     0     0     0     0
     1     1     0     0     0
     0     1     1     0     0
     0     0     1     1     0
     0     0     0     1     1
     0     0     0     0     1

Объяснение этого кода довольно простое. n обозначает, сколько раз вы хотели бы "повторить" вектор x влево и вправо, где каждый столбец циклически сдвигает элементы вверх или вниз в зависимости от того, в каком направлении в матрице вы движетесь.

Вторая строка кода является самой сложной. Давайте начнем с bsxfun(...) вызов:

bsxfun(@plus, (0:numel(x)-1).', n:-1:-n))

Это создает numel(x) x (2*n + 1) матрица, где каждый столбец просто вектор (0:numel(x)-1) но к этому добавляется постоянная ценность. Начиная с первого столбца добавляем n в (0:numel(x)-1)тогда второй столбец добавим n-1 в (0:numel(x)-1) пока мы не доберемся до середины, которая просто (0:numel(x)-1) само собой. После того, как вы пройдете середину, мы затем вычтем вектор с константой, исходя из -1 для n+1 колонка, -2 для n+2 колонка до конца. Результат, который мы получаем за n = 2 является:

ans =

     2     1     0    -1    -2
     3     2     1     0    -1
     4     3     2     1     0
     5     4     3     2     1
     6     5     4     3     2
     7     6     5     4     3

В идеальном мире мы в основном использовали бы эту матрицу для индексации нашего вектора, чтобы получить именно ту матрицу, которая нам нужна. Столбцы слева от середины постепенно обращаются к элементам, определяя индексы, которые увеличиваются на 1, и в результате элементы сдвигаются к вершине матрицы. Аналогично, столбцы справа от средних элементов прогрессивного доступа путем указания индексов, которые задерживаются на 1, и в результате сдвигают элементы к нижней части матрицы.

К сожалению, у нас есть как отрицательные значения, так и значения, которые превышают длину вектора. Вдобавок к этому MATLAB начинает индексирование с 1. Следовательно, вам придется использовать некоторую циклическую логику, чтобы гарантировать, что когда мы превысим длину вектора или создадим отрицательные значения для индекса, мы должны вместо этого обернуться в 1 0 или длина вектора вместо -1. Таким образом, мы можем просто разместить mod (модуль / остаток) операция, ограниченная общим количеством элементов в x, затем после добавления 1 ко всей матрице, чтобы мы могли ограничить индексы между 1 и общим количеством элементов, которые теперь приводят нас к завершенной второй строке кода:

>> ind = mod(bsxfun(@plus, (0:numel(x)-1).', n:-1:-n), numel(x)) + 1

ind =

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

Последний шаг - просто использовать эту матрицу для индексации вашего вектора для достижения желаемой выходной матрицы:

>> y = x(ind)

y =

     1     0     0     0     0
     1     1     0     0     0
     0     1     1     0     0
     0     0     1     1     0
     0     0     0     1     1
     0     0     0     0     1
Другие вопросы по тегам