2015-11-19 6 views
2

В Matlab у нас есть такой сценарий:Looping над вектором эффективно в MATLAB

v =[1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 .... N N N N]; 

где элементы в V всегда находятся в возрастающем порядке от 1 до N, и мы знаем, что значение N. Мы хотим сосчитать число «1 '„2“... в ст

Конечно, мы можем использовать цикл как следующие:.

for i = 1 : N 
    % method A 
    tic 
    ind = find(v == i) 
    ---> do sth with ind 
    t1 = toc; 

    % method B 
    tic 
    ind = v(v == i) 
    ---> do sth with ind 
    t2 = toc; 

    % method C 
    tic 
    ind = ismember(v , i) 
    ---> do sth with ind 
    t3 = toc; 


end 

время является необходимым для каждого из этой методы примерно равно $ t1 = 0,02 с $, $ t2 = 0,02 с $ и $ t3 = 0,03 с. В моей реальной работе N огромна, и весь цикл занимает от 2 до 3 часов!

Возможно, у вас есть идеи, что время для этого процесса может быть увеличено? Любая идея ценится.

ответ

6

Особый случай: Сортировано вход, Считает только

Если вы хотите получить отсчеты, несколько подходов могут быть предложены здесь.

Подход № 1:

accumarray(v(:),1) 

Подход № 2:

diff([0 find([diff(v) 1])]) 

Подход № 3:

histc(v,1:max(v)) 

Для выполнения, я бы поставил на diff, то accumarray и последний один на histc.


Общий случай: Unsorted вход, Считает & индексы

Для общего случая, когда входной вектор v не сортируется и может также потребоваться индексы, соответствующие каждой группе одинаковых чисел, вот один подход для хранения индексов в массиве ячеек -

[~,sort_idx] = sort(v); 
sorted_v = v(sort_idx); 
counts = diff([0 find([diff(sorted_v) 1])]) 
indices_per_grp = mat2cell(sort_idx,1,counts); 

Sample пробег -

v = 
    2  1  3  3  2  4  1  2  1  1  4  3  4 3 
counts = 
    4  3  4  3 
indices_per_grp{1} = 
    2  7  9 10 
indices_per_grp{2} = 
    1  5  8 
indices_per_grp{3} = 
    3  4 12 14 
indices_per_grp{4} = 
    6 11 13 
+1

Я согласен с вашим заказом на исполнение. +1. – rayryeng

+0

Но ОП попросил увеличить время! ;) – marsei

+1

@macduf Тогда OP будет 'fliplr' мой предложенный заказ;) – Divakar

0

Поскольку они отсортированы, вы можете сделать это намного эффективнее!

как о

lastfound = 1; 
for i = 1 : N 
    % find first location after current pos, where value is i 
    indStart = find(v(lastfound:end) == i, 1) 
    % find first location, after this, where value is not i 
    indEnd = find(v(indStart:end) ~= i, 1) 
    % now you have the range indStart:indEnd-1 
    ... 

    lastfound = indEnd; % start next one after the end of the current value 
end 

т.е. просто поиск вперед от последнего найденного элемента.

find(..., 1) находит только первый элемент, я считаю.

+0

Это почти делает то, что ОП уже пытались с 'find'.На самом деле, я уверен, что 'find' выполняет линейный поиск, поэтому даже если массив отсортирован,' find' даст вам такую ​​же сложность для поиска, независимо от того, в каком состоянии находится массив. – rayryeng

+0

Ну, дело в том, что это ищет только один раз по всему массиву, а не раз для каждого значения 'i'. Другими словами, он должен ускоряться в факторе 'N'. Поскольку find только ищет до точки, где происходит изменение следующего значения, это должно быть намного быстрее, не так ли? –

3

Я неравнодушен к bsxfun здесь:

counts = sum(bsxfun(@eq,v(:),1:max(v))); 
+0

Мне было интересно использовать bsxfcn. Кажется, однако, ваше предложение не готово к использованию: Ошибка при использовании bsxfun Не одиночные размеры двух входных массивов должны соответствовать каждому другим. – YAS

+0

@YAS Используйте 'sum (bsxfun (@ eq, v (:), 1: max (v))). Кажется, что ваш 'v' - это вектор столбца. – Divakar

+0

Спасибо. Я получаю эту ошибку памяти: Запрашиваемый массив 21622187x234934 (4730.9GB) превышает максимальный массив размер предпочтения. Создание массивов, превышающих этот предел, может занять долгое время и заставить MATLAB перестать отвечать на запросы. Для получения дополнительной информации см. Раздел . – YAS