У меня есть 2D-матрица, состоящая из единиц и нулей.
mat = [0 0 0 0 1 1 1 0 0
1 1 1 1 1 0 0 1 0
0 0 1 0 1 1 0 0 1];
Мне нужно найти все последовательные повторения единиц в каждой строке и заменить все единицы нулями Только, когда размер последовательности меньше 5 (5 последовательных):
mat = [0 0 0 0 0 0 0 0 0
1 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0];
Любое предложение о том, как подойти к этой проблеме, будет очень кстати.
Вы можете использовать diff
, чтобы найти начальную и конечную точки прогонов 1, и некоторую логику, основанную на этом, чтобы обнулить слишком короткие прогоны. См. приведенный ниже код с соответствующими комментариями.
% Input matrix of 0s and 1s
mat = [0 0 0 0 1 1 1 0 0
1 1 1 1 1 0 0 1 0
0 0 1 0 1 1 0 0 1];
% Minimum run length of 1s to keep
N = 5;
% Get the start and end points of the runs of 1. Add in values from the
% original matrix to ensure that start and end points are always paired
d = [mat(:,1),diff(mat,1,2),-mat(:,end)];
% Find those start and end points. Use the transpose during the find to
% flip rows/cols and search row-wise relative to input matrix.
[cs,r] = find(d.'>0.5); % Start points
[ce,~] = find(d.'<-0.5); % End points
c = [cs, ce]; % Column number array for start/end
idx = diff(c,1,2) < N; % From column number, check run length vs N
% Loop over the runs which didn't satisfy the threshold and zero them
for ii = find(idx.')
mat(r(ii),c(ii,1):c(ii,2)-1) = 0;
end
Если вы хотите выбросить читаемость из окна, это можно сжать для немного более быстрой и плотной версии, основанной на той же логике:
[c,r] = find([mat(:,1),diff(mat,1,2),-mat(:,end)].'); % find run start/end points
for ii = 1:2:numel(c) % Loop over runs
if c(ii+1)-c(ii) < N % Check if run exceeds threshold length
mat(r(ii),c(ii):c(ii+1)-1) = 0; % Zero the run if not
end
end
Конечно, MATLAB обычно работает по столбцам, поэтому я мог бы сделать [r,ce] = find(d>0.5)
, но значения r
(номер строки), например, были бы [2,2,2,3,2,1,2,3,...]
, поэтому все строки перемешаны, а соседние элементы не соответствуют начало/конец пробега в одном ряду. Однако, переворачивая его (и переворачивая выходные данные из строки/столбца в столбец/строку), мы получаем все индексы столбцов для строки 1, затем для всей строки 2 и т. д. Поскольку я следил за тем, чтобы всегда была парная начальная/ заканчиваются на d
, затем их можно сопоставить на [cs,ce]
без дальнейшей перетасовки
Возможно, был способ избежать более элегантного транспонирования, я не тратил много времени на его поиски, убедившись, что вышеизложенное сработает :)
Векторизованное решение @Wolfie красивое и лаконичное, но немного сложное для понимания и далекое от формулировки проблемы. Вот прямой перевод задачи с использованием циклов. Его преимущество в том, что его легче понять, и он немного быстрее с меньшим выделением памяти, что означает, что он будет работать с огромными входными данными.
[m,n] = size(mat);
for i = 1:m
j = 1;
while j <= n
seqSum = 1;
if mat(i,j) == 1
for k = j+1:n
if mat(i,k) == 1
seqSum = seqSum + 1;
else
break
end
end
if seqSum < 5
mat(i,j:j+seqSum-1) = 0;
end
end
j = j + seqSum;
end
end
У вас есть эталон для "немного быстрее"? Также достаточно легко предлагать дополнительные ответы, не пытаясь ухудшить существующие :)
Просто выполните tic
.. toc
или timeit
для широкого диапазона входных данных и сравните результаты. Я вижу около 6% ускорения с большим сокращением памяти в моей версии, потому что она не выделяет временные массивы. Это не имеет ничего общего с вашим решением, у каждого подхода есть свои достоинства и недостатки, это хорошо зарекомендовавшее себя наблюдение. См., например, этот ответ MATLAB и связанный с ними пример в. ..продолжение.
... Я использовал и преподавал MATLAB в течение двух десятилетий и не мог с первого взгляда понять ваше решение. Мой ответ, однако, больше похож на рассказ; просматривайте строки, всякий раз, когда вы находите 1
, отслеживайте следующие 1
, и если их меньше 5, обнуляйте их. Наконец, это хорошая практика и знания для других языков, таких как C/C++/Julia и т. д.
Спасибо, это работает как шарм! Не могли бы вы объяснить причину транспонирования при поиске начальной и конечной точек?