Estoy tratando de estimar la complejidad de un algoritmo que he escrito para el descompilador Reko , donde estoy tratando de "deshacer" la transformación realizada por un compilador a una división entera por una constante. El compilador ha convertido la división en una multiplicación entera y un cambio:, dónde es el número de bits de la palabra máquina de la computadora. La multiplicación constante resultante es mucho más rápida que una división en la mayoría de las arquitecturas contemporáneas, pero ya no se parece al código original.
Para ilustrar: la declaración C
y = x / 10;
será compilado por el compilador de Microsoft Visual C ++ al siguiente lenguaje ensamblador
mov edx,1999999Ah ; load 1/10 * 2^32
imul eax ; edx:eax = dividend / 10 * 2 ^32
mov eax,edx ; eax = dividend / 10
El resultado neto es que el registro eax
ahora tendrá el valor esperado del y
código fuente.
Un descompilador ingenuo descompilará lo anterior para
eax = ((long)eax * 0x1999999A) >> 32;
pero Reko tiene como objetivo hacer que la producción resultante sea más legible que eso mediante la recuperación de la constante que se utilizó en la división original.
El algoritmo mencionado anteriormente se basa en la descripción de este artículo en Wikipedia . Primero, el algoritmo trata el multiplicador constante como el recíproco escalado. Convierte eso en un número de coma flotante y luego lo reduce a , dónde . El paso final y costoso es poner entre corchetes el valor de coma flotante entre dos números racionales , (empezando por 0/1 y 1/1) y repetidamente calcular el mediant hasta que se alcance algún criterio de convergencia. El resultado debería ser la "mejor" aproximación racional al recíproco .
Ahora, si el horquillado se realiza con una búsqueda binaria típica que comienza entre los racionales y y calculando el punto medio , Espero que el algoritmo converja en pasos. Pero, ¿cuál es la complejidad del algoritmo si se usa el mediante?