2015-05-18 14 views
21

Ho una serie di valori doppi che desidero sommare e ottenere il valore massimo. I suoni DoubleStream.summaryStatistics() sono perfetti per questo. Il metodo getSum() ha una nota API che mi ricorda ciò che ho imparato durante uno dei miei corsi di informatica: la stabilità del problema di sommatoria tende a essere migliore se i valori sono ordinati in base ai loro valori assoluti. Tuttavia, DoubleStream non consente di specificare il comparatore da utilizzare, verrà utilizzato solo Double.compareTo se si chiama sorted() nello stream.Ordinamento (un flusso di) raddoppia in base alla magnitudine assoluta

Così ho raccolto i valori in un final Stream.Builder<Double> values = Stream.builder(); e chiamare

values.build() 
    .sorted(Comparator.comparingDouble(Math::abs)) 
    .mapToDouble(a -> a).summaryStatistics(); 

Eppure, questo sembra un po 'lungo e avrei preferito utilizzare il DoubleStream.Builder al posto del costruttore generico. Ho perso qualcosa o devo davvero usare la versione in scatola del flusso solo per poter specificare il comparatore?

+1

Hai un 'DoubleStream' dato a te usando un'API esterna o lo stai costruendo da solo? – Smutje

+0

Lo sto costruendo da solo. Potrei anche memorizzarli in una doppia []. Ho appena usato lo stream per summaryStatistics. – muued

risposta

13

I flussi primitivi non hanno un metodo sovraccarico sorted e verranno ordinati in ordine naturale. Ma per tornare al problema sottostante, ci sono modi per migliorare l'accuratezza della somma che non comporta l'ordinamento dei dati prima.

Uno di questi algoritmi è lo Kahan summation algorithm che viene utilizzato dallo OpenJDK/Oracle JDK internally.

Questo è certamente un dettaglio di implementazione in modo da applicare le solite avvertenze (non OpenJDK/Oracle JDK o futuri OpenJDK JDK possono assumere approcci alternativi etc.)

Vedi anche questo post: In which order should floats be added to get the most precise result?

+0

Quindi il tuo suggerimento è di usare semplicemente la funzione 'sum' e lasciare che il JDK faccia la parte difficile? Ciò significherebbe ignorare la nota dell'API e sollevare la domanda perché l'hanno messa lì in primo luogo. (L'implementazione dell'algoritmo di sommatoria di Kahan per il mio problema sembra reinventare la ruota) – muued

+1

@muued Questo è quello che faccio, perché so che (a) i miei utenti non usano JDK alternativi e (b) sono abbastanza fiducioso che OpenJDK non passare ad un algoritmo "peggiore" in futuro. Ma non c'è garanzia quindi è davvero la tua chiamata! Se non ti senti a tuo agio con le funzionalità non documentate puoi adattare (open source, ma GPL 2, che potrebbe non essere adatto a te) il codice di DoubleSummaryStatistics nella tua classe e chiamarlo con: 'doubleStream.collect (YourStats :: new , YourStats :: accept, YourStats :: combine); ' – assylias

+3

Se sei davvero paranoico, puoi anche testare l'implementazione all'avvio dell'applicazione. Anche se questo è un modo molto non Java di fare le cose. :) – biziclop

10

L'unico modo possibile ordinare DoubleStream è a box/unboxing esso:

double[] input = //... 
DoubleStream.of(input).boxed() 
    .sorted(Comparator.comparingDouble(Math::abs)) 
    .mapToDouble(a -> a).summaryStatistics(); 

Tuttavia come Kahan sommatoria viene utilizzato internamente, la differenza non dovrebbe essere molto significativo. Nella maggior parte delle applicazioni, l'input non ordinato produrrà la buona accuratezza risultante. Ovviamente dovresti provare da solo se la sommatoria non ordinata è soddisfacente per il tuo particolare compito.

Problemi correlati