2012-07-01 15 views
7

Lets have matrix A say A = magic(100);. Ho visto 2 modi di calcolare la somma di tutti gli elementi della matrice A.Modo efficiente (più veloce) per sommare elementi di matrice in matlab

sumOfA = sum(sum(A)); 

O

sumOfA = sum(A(:)); 

è uno dei più veloci (o meglio la pratica), poi l'altro? Se sì, qual è? O sono entrambi ugualmente veloci?

+0

Ogni metodo deve percorrere tutti gli elementi della matrice. Quindi sono uguali quando si tratta di complessità. Ti consiglierei di creare due script con metodi diversi, enormi matrici e calcolare il tempo di esecuzione. Prendendo una lunga distanza qui, direi che il secondo è migliore, dal momento che non coinvolge le operazioni di allocazione della memoria, ma come ho detto, è un campo lungo e potrei mancare qualcosa qui. –

+1

È possibile utilizzare le funzioni 'tic' e' toc' di Matlab per eseguire l'esperimento. – Turix

+3

Ho fatto un test rapido e non c'era differenza di velocità. Un vantaggio di 'sum (A (:))' è che non è necessario sapere quante dimensioni 'A' ha; funzionerà per qualsiasi numero di dim. – tmpearce

risposta

15

Sembra che non si può fare la tua mente su se le prestazioni o la precisione in virgola mobile è più importante.

Se la precisione in virgola mobile era di estrema precisione, si separano gli elementi positivi e negativi, ordinando ciascun segmento. Quindi sommare in ordine crescente di valore assoluto. Sì, lo so, è più lavoro di chiunque altro, e probabilmente sarà una perdita di tempo.

Invece, utilizzare una precisione adeguata in modo che eventuali errori effettuati siano irrilevanti. Utilizzare buone pratiche numeriche sui test, ecc., In modo tale che non ci siano problemi generati.

quanto riguarda il tempo passa, per una matrice NxM,

sum (A (:)) richiederà N * M-1 aggiunte.

somma (somma (A)) richiede (N-1) * M + M-1 = N * M-1 aggiunte.

Entrambi i metodi richiedono lo stesso numero di add, quindi per un array di grandi dimensioni, anche se l'interprete non è abbastanza intelligente da riconoscere che entrambi sono lo stesso operatore, a chi importa?

Non è semplicemente un problema. Non fare una montagna da una talpa per preoccuparti di questo.

Modifica: in risposta al commento di Amro sugli errori per un metodo rispetto all'altro, c'è poco che puoi controllare. Le aggiunte verranno eseguite in un ordine diverso, ma non vi è alcuna garanzia su quale sequenza sarà migliore.

A = randn(1000); 
format long g 

Le due soluzioni sono piuttosto vicine. In effetti, rispetto all'EPS, la differenza è appena significativa.

sum(A(:)) 
ans = 
      945.760668102446 

sum(sum(A)) 
ans = 
      945.760668102449 

sum(sum(A)) - sum(A(:)) 
ans = 
     2.72848410531878e-12 

eps(sum(A(:))) 
ans = 
     1.13686837721616e-13 

Supponiamo che tu scelga il trucco segregato e di ordinamento che ho menzionato. Vedi che le parti negative e positive saranno abbastanza grandi da far perdere precisione.

sum(sort(A(A<0),'descend')) 
ans = 
      -398276.24754782 

sum(sort(A(A<0),'descend')) + sum(sort(A(A>=0),'ascend')) 
ans = 
      945.7606681037 

Così realmente avrebbe bisogno di accumulare i pezzi in un array maggiore precisione in ogni caso. Potremmo provare questo:

[~,tags] = sort(abs(A(:))); 
sum(A(tags)) 
ans = 
      945.760668102446 

Un problema interessante si presenta anche in questi test. Ci sarà un problema perché i test sono fatti su un array casuale (normale)? In sostanza, possiamo visualizzare la somma (A (:)) come una passeggiata casuale, una passeggiata di un ubriaco. Ma considera la somma (somma (A)). Ogni elemento della somma (A) (cioè la somma interna) è esso stesso una somma di 1000 deviazioni normali. Guardate alcuni di loro:

sum(A) 
ans = 
    Columns 1 through 6 
     -32.6319600960983   36.8984589766173   38.2749084367497   27.3297721091922   30.5600109446534   -59.039228262402 
    Columns 7 through 12 
      3.82231962760523   4.11017616179294   -68.1497901792032   35.4196443983385   7.05786623564426   -27.1215387236418 
    Columns 13 through 18 

Quando li aggiungiamo, ci sarà una perdita di precisione. Quindi, potenzialmente, l'operazione come somma (A (:)) potrebbe essere leggermente più accurata. È così? Cosa succede se usiamo una precisione più elevata per l'accumulo? Quindi, per prima cosa, formerò la somma delle colonne usando il doppio, quindi convertirò in 25 cifre di precisione decimale e sommerò le righe. (Ho visualizzato solo 20 cifre qui, lasciando 5 cifre nascoste come cifra di guardia.)

sum(hpf(sum(A))) 
ans = 
945.76066810244807408 

O, invece, convertire immediatamente a 25 cifre di precisione, quindi sommando il risultato.

sum(hpf(A(:)) 
945.76066810244749807 

Quindi entrambe le forme in doppia precisione erano ugualmente sbagliate qui, in direzioni opposte. Alla fine, tutto è discutibile, dal momento che qualsiasi alternativa che ho mostrato richiede molto più tempo rispetto alla semplice somma delle variazioni (A (:)) o somma (somma (A)). Scegli uno di loro e non preoccuparti.

+0

Grazie, ottima risposta. Questo è il motivo per cui ho posto questa domanda: ho 2 pezzi di codice che entrambi pensano allo stesso modo (producono sempre lo stesso risultato). Ma una delle corse due volte più lunga dell'altra. Ci sono pochissime differenze tra loro e il modo in cui sommano le matrici era una di queste. Era mia opinione che potesse essere la parte che fa la differenza di prestazioni. Devo guardare da qualche altra parte. Le prestazioni sono così importanti qui perché è un problema di ottimizzazione su dati di grandi dimensioni ... E preferirei aspettare 1 giorno per il risultato di 2 giorni. – drasto

+2

@drasto Hai provato a profilare il tuo codice? Dici di avere "pochissime differenze tra loro", essendo questo solo uno. Hai guardato gli altri? Sarei molto sorpreso, anzi, sbalordito se il "collo di bottiglia" in un "problema di ottimizzazione su grandi dati" fosse la somma degli elementi di una matrice. – abcd

+0

Una preoccupazione che avrei con 'sum (sum (A))' è se l'interprete decide di allocare una matrice temporanea per contenere il risultato della 'somma' interna. Se stai facendo questo nel mezzo di un ciclo e hai un numero molto elevato di colonne, l'allocazione e la liberazione di quella matrice temporanea potrebbero trasformarsi in un vero e proprio trascinamento delle prestazioni. È possibile che il Matlab JIT possa ottimizzarlo, ma ho visto situazioni simili in cui non è successo. – sfstewman

3

Per quanto riguarda le prestazioni, direi che entrambi sono molto simili (supponendo una versione recente di MATLAB). Ecco test rapido utilizzando la funzione di TIMEIT:

function sumTest() 
    M = randn(5000); 
    timeit(@() func1(M)) 
    timeit(@() func2(M)) 
end 
function v = func1(A) 
    v = sum(A(:)); 
end 
function v = func2(A) 
    v = sum(sum(A)); 
end 

i risultati sono stati:

>> sumTest 
ans = 
    0.0020917 
ans = 
    0.0017159 

Quello che mi preoccuperei problemi in virgola mobile è. Esempio:

>> M = randn(1000); 
>> abs(sum(M(:)) - sum(sum(M))) 
ans = 
    3.9108e-11 

magnitudo errore aumenta per le matrici più grandi

+0

Quindi, per quanto riguarda i problemi a virgola mobile, quale metodo è preferibile? – drasto

+1

Non c'è modo di sapere quale darà una risposta migliore. I due risultati saranno completamente casuali nei bit di ordine inferiore. Dipende tutto dall'ordine in cui vengono aggiunti i numeri, che è qualcosa che non è matlab o di cui ti preoccuperai. –

Problemi correlati