2013-04-20 5 views
8

Mi piacerebbe un modo per calcolare (x + y)/2 per qualsiasi due interi x, y in Java. Il modo ingenuo soffre di problemi se x + y> Integer.MAX_VALUE o < Integer.MIN_VALUE.Media di due inte (o long) senza overflow, troncata verso 0

Guava IntMathuses questa tecnica:

public static int mean(int x, int y) { 
    // Efficient method for computing the arithmetic mean. 
    // The alternative (x + y)/2 fails for large values. 
    // The alternative (x + y) >>> 1 fails for negative values. 
    return (x & y) + ((x^y) >> 1); 
    } 

... ma questo arrotonda verso l'infinito negativo, il che significa la routine non è d'accordo con il modo ingenuo per i valori come {-1, -2} (dando -2, anziché -1).

C'è qualche routine corrispondente che tronca verso 0?

"Basta usare long" non è la risposta che sto cercando, poiché voglio un metodo che funzioni anche per gli input lunghi. BigInteger non è la risposta che sto cercando. Non voglio una soluzione con nessun ramo.

+0

* "Non voglio una soluzione con i rami". * - anche se non la migliore soluzione senza rami è più lenta la migliore soluzione con rami ? –

+2

Ecco una soluzione per C++: http://stackoverflow.com/a/3816473/139985. Dovrebbe funzionare anche per Java. –

+0

Hai ragione - se c'è una soluzione con rami che funzionano meglio di una senza ramo su input casuali, sono felice di usarla. Immagino che stia mostrando il mio pregiudizio - dubito che esista una soluzione :) – BeeOnRope

risposta

2

È necessario aggiungere 1 al risultato se i bit più bassi sono diversi (quindi il risultato non è esatto e occorre arrotondare) e il bit di segno nel risultato è impostato (il risultato è negativo, quindi si desidera per cambiare il round in un round up).

Così dovrebbe fare quanto segue (non testato):

public static int mean(int x, int y) { 
    int xor = x^y; 
    int roundedDown = (x & y) + (xor >> 1); 
    return roundedDown + (1 & xor & (roundedDown >>> 31)); 
} 
+0

Non sono riuscito a trovare nulla più velocemente. – BeeOnRope

0

Perché non si fa qualcosa come (x-y)/2 + y, che si riduce a x/2 - y/2 + y = x/2 + y/2? Quindi se x+y ti dà un overflow o underflow, lo fai nel modo (x-y)/2 + y.

+1

'x-y' può underflow e questo è problematico allo stesso modo dell'originale. Inoltre, i rami sono molto lenti se sono mal previsti. – BeeOnRope

+0

Penso che se 'x + y' overflow/underflow, non è possibile che' xy' abbia underflow/overflow ... – Anjoola

+0

Sicuro, ma ciò significa che è necessario fare un controllo per vedere se x + y trabocca, introducendo un ramo, che sarà molto poco previsto se i dati di input sono distribuiti casualmente (dal momento che molte combinazioni avranno un over/underflow). Ecco perché ho detto "niente rami". – BeeOnRope