Sto cercando di stimare la complessità di un algoritmo che ho scritto per il decompilatore Reko , in cui sto cercando di "annullare" la trasformazione eseguita da un compilatore in una divisione intera di una costante . Il compilatore ha convertito la divisione in una moltiplicazione intera e uno spostamento: , dove è il numero di bit della parola macchina del computer. La moltiplicazione costante risultante è molto più veloce di una divisione nella maggior parte delle architetture contemporanee, ma non assomiglia più al codice originale.
Per illustrare: la dichiarazione C.
y = x / 10;
verrà compilato dal compilatore Microsoft Visual C ++ nel seguente linguaggio assembly
mov edx,1999999Ah ; load 1/10 * 2^32
imul eax ; edx:eax = dividend / 10 * 2 ^32
mov eax,edx ; eax = dividend / 10
Il risultato netto è che il registro eax
avrà ora il valore atteso y
dal codice sorgente.
Un ingenuo decompilatore decompilerà quanto sopra
eax = ((long)eax * 0x1999999A) >> 32;
ma Reko mira a rendere l'output risultante più leggibile di quello recuperando la costante utilizzata nella divisione originale.
L'algoritmo accennato sopra si basa sulla descrizione di questo articolo su Wikipedia . Innanzitutto, l'algoritmo considera il moltiplicatore costante come il reciproco ridimensionato . Lo converte in un numero a virgola mobile e quindi lo ridimensiona di in , dove . Il passaggio finale, costoso, è di racchiudere il valore in virgola mobile tra due numeri razionali , (a partire da 0/1 e 1/1) e calcolare ripetutamente il mediante fino a quando non viene raggiunto un criterio di convergenza. Il risultato dovrebbe essere il "migliore" approssimazione razionale al reciproco .
Ora, se il bracketing veniva eseguito con una tipica ricerca binaria che iniziava tra le razionali e e calcolava il punto medio , Mi aspetto che l'algoritmo converga in passaggi . Ma qual è la complessità dell'algoritmo se si utilizza invece la mediant?