2012-04-13 11 views
12

Widziałem kilka pytań o symulacje i animacje w JavaScript, który często wiąże obliczania przeciwprostokątną:Najszybsza przeciwprostokątna w javascript?

hypot = Math.sqrt(x*x + y*y); 

Ponieważ współrzędne kartezjańskie są bronią w większości z tych silników, obliczenia te są potrzebne do znajdź odległość między parami punktów itp. Więc każde przyspieszenie w obliczaniu przeciwprostokątnej może być bardzo pomocne w wielu projektach.

W tym celu widzisz szybszą metodę niż powyższa prosta implementacja? Zauważyłem przybliżenie, które było nieznacznie szybsze w Chrome, ale w Firefoksie okazało się znacznie wolniejsze, oparte na this approximation function in SuperCollider.

Edycja 2015-08-15: Zmieniłem zaakceptowaną odpowiedź na bemię Math.hypot; Podejrzewam, że obecnie pragmatyczne podejście polegałoby na używaniu Math.hypota lub syntezowanej funkcji hipotetycznej, jeśli nie jest dostępna, i na porównaniu z kwadratem (na odpowiedź per sch), jeśli to wystarczy i Math.hypot nie jest dostępny.

+3

zawsze można użyć [niektóre 0x5f3759df magia] (http://en.wikipedia.org/wiki/Fast_inverse_square_root) – violet313

+1

To szlachetny pan chce przyspieszyć każdy skrypt, który używa formuły Pitagorasa. Jednak nie sądzę, aby istniało ogólne rozwiązanie, które przyspieszyłoby formułowanie (w przeciwnym razie nie używalibyśmy wersji 2500-letniej). Zamiast próbować przyspieszyć formułę, spróbuj zmienić kod, aby użyć formuły mniej i dopiero po wykazaniu, że formuła jest wąskim gardłem w wydajności twojego kodu. – Kevin

+0

@Kevin: W języku C lub innym języku z niższymi narzutami, są rzeczywiście przybliżenia, które przyspieszają. To, czy przybliżenie jest użyteczne, zależy od dokładności wymaganej przez model, ale w przypadku fizyki gry byłoby ogólnie rzecz biorąc opłacalne, gdyby gra była bardziej płynna. –

Odpowiedz

1

W ECMAScript ES6 można użyć Math.hypot:

// ES5 support 
 

 
Math.hypot = Math.hypot || function(x, y){ return Math.sqrt(x*x + y*y) } 
 

 
var x = 3, y = 4; 
 

 
document.write(Math.hypot(x, y))

Edit: Można uruchomić ten test na karcie pusty, są 2 mln operacje zarówno metody, wyniki są bardzo dobre, jest o 24% szybsze.

var i, tmp, x = 55, y = 66, end, ini = performance.now(); 

// Math.sqrt operation 
i = 0; 
ini = performance.now(); 
tmp = 0; 
while(i++ < 2000000){ 
    tmp += Math.sqrt(x*x + y*y) 
} 
end = performance.now(); 
console.log(tmp, "Math.sqrt operation: " + (end - ini) + " ms"); 

// Math.hypot 

i = 0; 
ini = performance.now(); 
tmp = 0; 
while(i++ < 2000000){ 
    tmp += Math.hypot(x, y) 
} 
end = performance.now(); 

console.log(tmp, "Math.hypot: " + (end - ini) + " ms"); 

Uwaga: W tym teście jest używany ES6 za Math.hypot.

enter image description here

+0

Ooh, jakikolwiek pomysł na wykonanie tego? –

+0

@PhilH Edytuj, testowałem operację z Math.sqrt i Math.hypot, możesz to zobaczyć. W tym teście używana jest oryginalna funkcja Math.hypot: –

+0

'hypot' pojawia się konsekwentnie wolniej w Chrome 62. Starałem się umieścić go w ścisłej funkcji, aby zminimalizować zewnętrzne zakłócenia (https://gist.github.com/anonymous/159187ac9a8d3caf97737cd9cf551c1a), ale sqrt pozostaje szybszą operacją. Być może w pewnym momencie dodali jakąś ochronę przed przepełnieniem/niedopełnieniem? – Arthur2e5

13

Często nie trzeba obliczać pierwiastka kwadratowego i wystarczy hypot^2 = x*x + y*y. Dzieje się tak na przykład, jeśli chcesz porównać odległości i nie potrzebujesz rzeczywistych wartości.

0

Możesz patrzeć na równość x i y. Jeśli są równe, można obliczyć przeciwprostokątną jako (x + y)/sqrt(2), gdzie sqrt(2) jest stałą.

Tak więc tej metody można użyć w przypadku, gdy x = y. W innych przypadkach można go stosować z maksymalną niedokładnością około 41%. To jest duży błąd. Ale kiedy określasz dopuszczalne limity błędów, możesz użyć tej metody. Na przykład, jeśli zdefiniuj dopuszczalny błąd 5%, możesz uzyskać, że b musi znajdować się między 0.515*a a 1.942*a.

Jeśli nie potrzebujesz doskonałego nieprecyzyjności swoich obliczeń, możesz poprawić wydajność obliczeń z zakresu wartości.

Przez analogię można patrzeć na równość x lub y na zero. I z pewną dokładnością obliczyć przeciwprostokątną szybciej w tych przypadkach.

P.S. Przeczytałem o tym w tym russian article.

6

Ważnym punktem, że wiele z nich nie wiem:

hypot = Math.sqrt(x*x + y*y);

To działa w teorii, ale w praktyce może się nie powieść. Jeśli x jest tak duże, że x * x jest przepełnione, kod wygeneruje nieskończony wynik.

Oto jak obliczyć sqrt (x x + y y) bez ryzyka przepełnienia.

max = maximum(|x|, |y|) 
min = minimum(|x|, |y|) 
r = min/max 
return max*sqrt(1 + r*r) 

referencyjny i kompletny tekst: John D.Cook - http://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse/

+0

(plus jeden) dobrze jest wspomnieć o problemach związanych z implementacją, takich jak przepełnione obliczenia itp. –

+0

plus jeden szczegół, upewnij się, że przynajmniej jeden z 'x' i' y' jest niezerowe, inaczej będzie to "nieskończoność" –

Powiązane problemy