2012-04-12 17 views
7

Ho un ciclo di controllo in esecuzione ad alta frequenza e ho bisogno di calcolare una radice quadrata per ogni ciclo. Le funzioni tipiche della radice quadrata funzionano bene ma richiedono un tempo eccessivo. Dal momento che il valore che sto prendendo la radice quadrata di non cambia troppo in ogni ciclo, vorrei trovare una radice quadrata iterativa che convergerà e quindi traccia il risultato corretto. In questo modo ho potuto fare una singola iterazione ad ogni passo temporale, piuttosto che molti.radice quadrata di rilevamento del valore mobile

Il problema è che probabilmente tutti i metodi di radice quadrata iterativi che ho visto avranno esito negativo quando l'input sta cambiando. In particolare sembra che ci saranno problemi quando l'input va a zero e poi aumenta nuovamente - i metodi non amano iniziare con un'ipotesi di zero.

Il mio intervallo di input è 0-4,5 e ho bisogno di una precisione di circa 0,01, quindi l'utilizzo di un incremento/decremento di 0,01 potrebbe richiedere troppo tempo - voglio che converga principalmente in 10 cicli o meno.

FYI Sto usando il punto fisso 16/32 bit l'ingresso è 16 bit q12. È su un microcontrollore quindi non mi interessa usare 1K per una tabella di ricerca. Il codice viene generato anche da un modello simulink e le loro funzioni di ricerca tabella sono piuttosto piene di overhead.

C'è una buona soluzione a questo?

+0

Un colpo del metodo di Halley (http://www.mathpath.org/Algor/squareroot/algor.square.root.halley.htm) dovrebbe fare bene. Se vuoi evitare la divisione, aggiorna 1/sqrt (x) e usa Newton o Halley. –

+0

Cosa vuoi dire, il valore cambia? Stai dicendo che vuoi trovare 'sqrt (x + epsilon)' conoscendo 'x' e' sqrt (x) 'senza dover calcolare direttamente?O stai dicendo che il registro che contiene x è volatile e può cambiare nel mezzo del calcolo (!?!)? –

+0

Guarda questa funzione 'FastSqrt' usata nei giochi http://www.gamedev.net/topic/278840-fast-sqrt/ – ja72

risposta

1

È possibile utilizzare uno scatto del metodo Halley. Ha convergenza cubica e pertanto dovrebbe essere abbastanza preciso se il valore si muove leggermente:

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

Questo converge cubcially a sqrt(Q).

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

+0

Le mie simulazioni mostrano che funziona al meglio. È anche l'unica cosa che ho provato che funziona abbastanza vicino allo zero. x_n deve essere ritagliato su un valore piccolo almeno nel feedback per impedire la divisione per zero. – phkahler

+0

Se si tratta di un punto mobile, è possibile dimezzare l'esponente dell'ingresso e utilizzare il valore risultante come prima stima da inserire in questo o in qualsiasi altro algoritmo di radice quadrata iterativo. –

+0

@R .. L'ipotesi iniziale è l'ultimo valore aggiornato (vedi domanda). Ma sì, in un contesto più generico funzionerebbe molto bene. –

5

l'intervallo 0-4,5 è abbastanza piccolo. Con una precisione di 0,01, sono solo 450 i possibili calcoli. Potresti calcolarli tutti in fase di compilazione come costanti e fare semplicemente una ricerca durante il runtime.

+0

Troppa memoria: modifica la domanda per riflettere questo. – phkahler

+0

Hai provato a postare questa domanda o qualcosa di simile al sito di scambio di calcoli matematici? –

2

Suggerirei di utilizzare una tabella di ricerca, se si conoscono in anticipo gli intervalli con cui si ha a che fare. Genera un array o una tabella hash (a seconda della lingua in cui stai lavorando) con il livello di precisione necessario e fai riferimento a questo quando hai bisogno delle tue radici.

2

Ho provato un secondo ordine di espansione di Taylor su sqrt(x) e passare il seguente risultato

se y=sqrt(x) e sai y_c = sqrt(x_c) già allora:

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

Maggiore x è il migliore è l'approssimazione. Nel caso peggiore con x_c=0.01 e x=0.02, il risultato risulta 0.1375 rispetto al risultato reale di sqrt(0.02)=0.1414 o una differenza di 0.0039 che è inferiore a 0.01.

Ho provato il codice con C# e ho visto un costante 33% speedup vs Math.Sqrt().

+0

Vedrò questo. La divisione è indesiderabile ma potrebbe essere OK. Cosa fa quando l'input è zero per un po '? – phkahler

+0

@phkahler: scorciatoia lo zero per restituire '0' duh! se 'y_c' è zero devi valutare' sqrt() '. – ja72

+0

@phkahler: avrai comunque bisogno della divisione se stai calcolando 'sqrt (x)' (puoi evitarlo se stai calcolando 1/sqrt (x)). –

Problemi correlati