2016-03-11 9 views
5

Ho la seguente matrice in Matlab:Trovare l'indice di colonna per il 1 in ogni riga della matrice

M = [0 0 1 
    1 0 0 
    0 1 0 
    1 0 0 
    0 0 1]; 

Ogni riga ha esattamente un 1. Come posso (senza loop) determinare un vettore colonna in modo che il primo elemento è un 2 se c'è un 1 nella seconda colonna, il secondo elemento è un 3 per uno nella terza colonna, ecc.? L'esempio sopra dovrebbe trasformarsi in:

M = [ 3 
     1 
     2 
     1 
     3]; 

risposta

5

In realtà è possibile risolvere questo problema con una semplice moltiplicazione di matrice.

result = M * (1:size(M, 2)).'; 

    3 
    1 
    2 
    1 
    3 

Questo funziona moltiplicando il M x 3 matrice 3 x 1 array in cui gli elementi del 3x1 sono semplicemente [1; 2; 3]. In breve, per ogni riga di M, la moltiplicazione a livello di elemento viene eseguita con l'array 3 x 1. Solo gli 1 nella riga di M generano qualcosa nel risultato. Quindi viene sommato il risultato di questa moltiplicazione di elementi. Poiché hai un solo "1" per riga, il risultato sarà l'indice della colonna in cui si trova 1.

Ad esempio, per la prima riga di M.

element_wise_multiplication = [0 0 1] .* [1 2 3] 

    [0, 0, 3] 

sum(element_wise_multiplication) 

    3 

Aggiornamento

Sulla base delle soluzioni fornite da @reyryeng e @Luis seguito, ho deciso di eseguire un confronto per vedere come le prestazioni dei vari metodi a confronto.

Per impostare la matrice di test (M) ho creato una matrice del modulo specificato nella domanda originale e ho variato il numero di righe. Quale colonna ha scelto l'1 in modo casuale usando randi([1 nCols], size(M, 1)). I tempi di esecuzione sono stati analizzati utilizzando timeit.

Quando si utilizza M di tipo double (impostazione predefinita di MATLAB) si ottengono i seguenti tempi di esecuzione.

enter image description here

Se M è un logical, quindi la moltiplicazione matrice subisce un colpo a causa del fatto che esso deve essere convertito in un tipo numerico prima matrice moltiplicazione, mentre gli altri due hanno un po 'di un miglioramento delle prestazioni.

enter image description here

Ecco il codice di prova che ho usato.

sizes = round(linspace(100, 100000, 100)); 
times = zeros(numel(sizes), 3); 

for k = 1:numel(sizes) 
    M = generateM(sizes(k)); 
    times(k,1) = timeit(@()M * (1:size(M, 2)).'); 
    M = generateM(sizes(k)); 
    times(k,2) = timeit(@()max(M, [], 2), 2); 
    M = generateM(sizes(k)); 
    times(k,3) = timeit(@()find(M.'), 2); 
end 

figure 
plot(range, times/1000); 
legend({'Multiplication', 'Max', 'Find'}) 
xlabel('Number of rows in M') 
ylabel('Execution Time (ms)') 

function M = generateM(nRows) 
    M = zeros(nRows, 3); 
    col = randi([1 size(M, 2)], 1, size(M, 1)); 
    M(sub2ind(size(M), 1:numel(col), col)) = 1; 
end 
+0

Grazie mille per la tua risposta molto veloce. È perfetto. – machinery

2

Si può anche abusare find e osservare la fila posizioni della trasposta di M. È necessario trasporre la matrice prima come find opera in modo importante della colonna:

M = [0 0 1 
    1 0 0 
    0 1 0 
    1 0 0 
    0 0 1]; 

[out,~] = find(M.'); 

Non so se questo è più veloce di moltiplicazione di matrici però.

+1

'find' è una delle rare occasioni in cui' [out, ~] = ... 'non è uguale a' out = ... ' –

+0

@LuisMendo aha si signore! – rayryeng

2

Ancora un altro approccio: usa la seconda uscita del max:

[~, result] = max(M.', [], 1); 

Oppure, come suggerito da @rayryeng, utilizzare max lungo la seconda dimensione invece di trasposizione M:

[~, result] = max(M, [], 2); 

Per

M = [0 0 1 
    1 0 0 
    0 1 0 
    1 0 0 
    0 0 1]; 

questo dà

result = 
    3  1  2  1  3 

Se M contiene più di un 1 in una determinata riga, questo darà l'indice del primo di tali 1.

+0

Ben fatto. Mi dimentico sempre di usare 'max' in questo modo. Mi chiedo quale sia il confronto delle prestazioni tra tutti gli approcci. I miei soldi sono al massimo ' – rayryeng

+2

C'è una differenza di prestazioni se non hai trasposto la matrice e specificato invece guardando lungo le righe della matrice? Come specificare la dimensione da considerare come '2'? – rayryeng

+0

@rayryeng Buon punto. Probabilmente più veloce da non trasporre, sì –

Problemi correlati