Trasformare una matrice binaria in un vettore dell'ultimo indice diverso da zero in modo rapido e vettoriale

StackOverflow https://stackoverflow.com/questions/831721

Domanda

Supponiamo, in MATLAB, che io abbia una matrice, A, i cui elementi sono 0 o 1.

Come posso ottenere un vettore dell'indice dell'ultimo elemento diverso da zero di ogni colonna in un modo più veloce e vettoriale?

Potrei farlo

[B, I] = max (cumsum (A));

e usa I , ma c'è un modo più veloce? (Suppongo che il cumsum costerebbe un po 'di tempo anche a sommare 0 e 1).

Modifica: immagino di aver vettorializzato anche più di quanto mi serva velocemente - il loop di Mr. Fooz è fantastico ma ogni loop in MATLAB sembra costare me molto in tempo di debug anche se è veloce.

È stato utile?

Soluzione

Come mostrato da Mr Fooz , poiché i loop possono essere abbastanza veloci ora con le versioni più recenti di MATLAB. Tuttavia, se vuoi davvero avere un codice vettoriale compatto, ti suggerisco di provare questo:

[B,I] = max(flipud(A));
I = size(A,1)-I+1;

Questo è più veloce della tua risposta basata su CUMSUM, ma non è altrettanto veloce delle opzioni di looping di Mr Fooz.

Altre due cose da considerare:

  • Quali risultati vuoi ottenere per una colonna che non ne contiene affatto? Con l'opzione sopra che ti ho dato, credo che otterrai un indice di dimensione (A, 1) (cioè il numero di righe in A ) in tal caso. Per la tua opzione, credo che otterrai un 1 in questo caso, mentre l'opzione annidata per i cicli di Mr Fooz ti darà uno 0.

  • La velocità relativa di queste diverse opzioni varierà probabilmente in base alla dimensione di A e al numero di zero diversi che ci si aspetta che abbia.

Altri suggerimenti

Veloce è ciò di cui dovresti preoccuparti, non necessariamente la vettorializzazione completa. Le versioni recenti di Matlab sono molto più intelligenti sulla gestione efficiente dei loop. Se esiste un modo vettorializzato compatto per esprimere qualcosa, di solito è più veloce, ma i loop non dovrebbero (sempre) essere temuti come una volta.

clc

A = rand(5000)>0.5;
A(1,find(sum(A,1)==0)) = 1; % make sure there is at least one match

% Slow because it is doing too much work
tic;[B,I1]=max(cumsum(A));toc

% Fast because FIND is fast and it runs the inner loop
tic;
I3=zeros(1,5000);
for i=1:5000
  I3(i) = find(A(:,i),1,'last');
end
toc;
assert(all(I1==I3));

% Even faster because the JIT in Matlab is smart enough now
tic;
I2=zeros(1,5000);
for i=1:5000
  I2(i) = 0;
  for j=5000:-1:1
    if A(j,i)
      I2(i) = j;
      break;
    end
  end
end
toc;
assert(all(I1==I2));

Su R2008a, Windows, x64, la versione cumsum richiede 0,9 secondi. La versione loop and find richiede 0,02 secondi. La versione a doppio loop richiede solo 0,001 secondi.

MODIFICA: quale è il più veloce dipende dai dati effettivi. Il doppio loop impiega 0,05 secondi quando si cambia da 0,5 a 0,999 (perché impiega più tempo a raggiungere l'interruzione; in media). cumsum e l'implementazione loop & amp; find hanno velocità più coerenti.

EDIT 2: la soluzione flipud di gnovice è intelligente. Sfortunatamente, sulla mia macchina di prova ci vogliono 0,1 secondi, quindi è molto più veloce del cumsum, ma più lento delle versioni in loop.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top