2012-04-12 25 views
7

Mam pętlę sterowania działającą z wysoką częstotliwością i trzeba obliczyć pierwiastek kwadratowy każdego cyklu. Typowe pierwiastki kwadratowe działają dobrze, ale zajmują zbyt wiele czasu. Ponieważ wartość, którą biorę pierwiastek kwadratowy nie zmienia się zbytnio w każdym cyklu, chciałbym znaleźć iteracyjny pierwiastek kwadratowy, który zbiegnie się, a następnie prześledzi poprawny wynik. W ten sposób mogę wykonać pojedynczą iterację za każdym razem, a nie za wiele.śledzenie pierwiastka kwadratowego z ruchomej wartości

Problem polega na tym, że wszystkie powtarzające się pierwiastki kwadratowe, które widziałem, prawdopodobnie zawiodą, gdy dane wejściowe ulegną zmianie. W szczególności wygląda na to, że pojawią się problemy, gdy wejście zmieni się na zero, a następnie ponownie wzrośnie - metody nie lubią zaczynać od zgadywania zerowego.

Mój zakres wejściowy wynosi 0-4.5 i potrzebuję precyzji około 0,01, więc użycie inkrementu/dekrementacji o wartości 0,01 może potrwać zbyt długo - chcę, aby w większości zbiegał się z 10 cyklami lub mniej.

FYI Używam stałego punktu 16/32bit, wejście to 16bit q12. Jest na mikrokontrolerze, więc nie jestem zainteresowany wykorzystaniem 1K do tabeli odnośników. Kod jest również generowany z modelu Simulink, a jego funkcje wyszukiwania tabel są dość pełne narzutów.

Czy jest to dobre rozwiązanie?

+0

Jeden strzał Metoda Halleya (http://www.mathpath.org/Algor/squareroot/algor.square.root.halley.htm) powinien robić dobrze. Jeśli chcesz uniknąć dzielenia, zaktualizuj 1/sqrt (x) zamiast tego i użyj Newtona lub Halleya. –

+0

Co masz na myśli, zmiana wartości? Mówisz, że chcesz znaleźć 'sqrt (x + epsilon)' wiedząc 'x' i' sqrt (x) 'bez konieczności obliczania go bezpośrednio?Czy możesz powiedzieć, że rejestr zawierający x jest niestabilny i może zmienić się w środku obliczeń (!?!)? –

+0

Zobacz funkcję 'FastSqrt' używaną w grach http://www.gamedev.net/topic/278840-fast-sqrt/ – ja72

Odpowiedz

1

Możesz użyć jednego ujęcia metody Halleya. Ma sześcienny konwergencji, a zatem powinno być dość precyzyjne, jeżeli wartość przenosi się nieznacznie:

x_{n+1} = x_n * (x_n^2 + 3Q)/(3 x_n^2 + Q) 

ta zbiega cubcially do sqrt(Q).

referencyjny: http://www.mathpath.org/Algor/squareroot/algor.square.root.halley.htm

+0

Moje symulacje pokazują, że to działa najlepiej. Jest to także jedyna rzecz, którą wypróbowałem, która działa rozsądnie blisko zera. x_n musi zostać przycięty do małej wartości przynajmniej w sprzężeniu zwrotnym, aby zapobiec dzieleniu przez zero. – phkahler

+0

Jeśli jest to zmiennoprzecinkowy, można po prostu zmniejszyć o połowę wykładnik danych wejściowych i wykorzystać uzyskaną wartość jako pierwszą wartość szacunkową do wprowadzenia do tego lub dowolnego innego iteracyjnego algorytmu pierwiastkowego. –

+0

@R .. Początkowe odgadnięcie jest ostatnią zaktualizowaną wartością (patrz pytanie). Ale tak, w bardziej ogólnikowym ustawieniu działałoby to bardzo dobrze. –

5

Zakres 0-4.5 jest dość mały. Z dokładnością do 0,01, to tylko 450 możliwych obliczeń. Można je obliczyć w czasie kompilacji jako stałe i po prostu sprawdzić w czasie wykonywania.

+0

Za dużo pamięci - zredagowano pytanie, aby to odzwierciedlić. – phkahler

+0

Czy próbowałeś opublikować to pytanie lub coś podobnego do strony wymiany stosu matematyki? –

2

Proponuję użyć tabeli odnośników, jeśli znasz zaawansowane zakresy, z którymi masz do czynienia. Wygeneruj tablicę lub tabelę mieszania (w zależności od języka, w którym pracujesz) do poziomu precyzji, którego potrzebujesz i odnosisz się do tego, kiedy potrzebujesz swoich korzeni.

2

Próbowałem drugi rozkaz Taylora ekspansję na sqrt(x) i udać się następujący wynik

jeśli y=sqrt(x) i wiesz y_c = sqrt(x_c) już wówczas:

t = x-3*x_c; 
y = (12*x_c*x_c-t*t)/(8*y_c*y_c*y_c); 

Im większy x jest lepsze przybliżenie. W najgorszym przypadku z x_c=0.01 i x=0.02 wynik wychodzi 0.1375 vs rzeczywisty wynik sqrt(0.02)=0.1414 lub różnica 0.0039, która jest pod 0.01.

Przetestowałem kod za pomocą C# i zobaczyłem stabilne przyspieszenie 33% vs Math.Sqrt().

+0

Zajrzę do tego. Podział jest niepożądany, ale może być OK. Co robi, gdy wejście przez chwilę jest zerowe? – phkahler

+0

@phkahler: Skróć zero, aby zwrócić '0' duh! jeśli 'y_c' wynosi zero, musisz ocenić' sqrt() '. – ja72

+0

@phkahler: i tak będziesz potrzebować podziału, jeśli obliczasz 'sqrt (x)' (możesz tego uniknąć, jeśli obliczasz 1/sqrt (x)). –

Powiązane problemy