El algoritmo de división es un algoritmo que calcula para dos enteros dados y su cociente y/o resto, el resultado de la división con un resto de . Algunos de los algoritmos están diseñados para cálculos manuales, otros se implementan en circuitos digitales y software.
Los algoritmos de división se dividen en dos grandes categorías: división lenta y división rápida. Los algoritmos de división lenta producen un dígito del resultado por iteración. Ejemplos de división lenta son los algoritmos de división con recuperación , sin recuperación y SRT . Los métodos de división rápida comienzan aproximando el cociente final y producen el doble de dígitos en el resultado final en cada iteración. Los algoritmos de Newton-Rapson y Goldschmidt entran en esta categoría.
Las variantes de estos algoritmos permiten el uso de algoritmos de multiplicación rápida . Como resultado, para números enteros grandes, el tiempo de cálculo requerido para la división será el mismo (hasta un factor constante) que el tiempo requerido para realizar la multiplicación, cualquiera que sea el algoritmo de los enumerados que no se aplique.
La discusión utilizará la notación donde
son números de entrada, y
son la salida.
El algoritmo más simple, históricamente integrado en el algoritmo del máximo común divisor y presentado en los Principia de Euclides , Libro VII Proposición 1, encuentra el resto de una división de dos números enteros positivos usando solo la resta y la comparación:
R : = N mientras que R >= D hacer R : = R − D final retorno RLa prueba de que el cociente y el resto existen y son únicos (descrito en el artículo División con resto ) da un algoritmo de división completo basado en sumas, restas y comparaciones:
función dividir ( N , D ) si D = 0 entonces error ( DivisionByZero ) terminar si D < 0 entonces ( Q , R ) : = dividir ( N , − D ); volver ( − Q , R ) terminar si N < 0 entonces ( Q , R ) : = dividir ( − N , D ) si R = 0 entonces volver ( − Q , 0 ) si no volver ( − Q − 1 , D − R ) end end -- Aquí N >= 0 y D >= 0 devuelven divide_unsigned ( N , D ) end función divide_unsigned ( N , D ) Q : = 0 ; R : = N while R >= D do Q : = Q + 1 R : = R − D fin volver ( Q , R ) finEste procedimiento siempre da . Al ser muy simple, el algoritmo requiere pasos y, por lo tanto, es exponencialmente más lento que algoritmos como la división larga. El algoritmo es útil si se sabe que (los pasos) son pocos (dependiendo del volumen de salida).
La división larga es un algoritmo estándar de números decimales de varios dígitos que se utiliza para la división con lápiz y papel. Se desplaza gradualmente de izquierda a derecha desde el dividendo, restando el mayor múltiplo posible del divisor en cada paso. El multiplicador se convierte en un cociente y la diferencia final se convierte en el resto de la división.
Cuando el algoritmo se usa en base 2, este método forma la base para dividir números naturales con un resto. La división corta es una variante de la división larga, adecuada para dividir por un solo dígito. El algoritmo de decremento , también conocido como el método del cociente fraccionario, es un tipo de división por columna menos eficiente, pero es más fácil de entender. Permitir que se reste un múltiplo más grande que el que se hace en cada paso permite más libertad para crear variantes de división larga.
El algoritmo anterior, una versión binaria de la conocida división en longitud , divide colocando el cociente en y el resto en . Todos los valores se tratan como enteros sin signo.
si D = 0 entonces error ( DivisionByZeroException ) end -- División por cero Q : = 0 -- Los valores iniciales del cociente y resto son 0 R : = 0 para i : = n − 1 .. 0 do -- Aquí n es un bit numérico en N R : = R << 1 -- Desplazamiento a la izquierda de R en 1 bit R ( 0 ) : = N ( i ) -- Iguala el bit menos significativo de R al bit i del dividendo si R >= D entonces R : = R − D Q ( i ) : = 1 fin fin EjemploToma ( ) y ( )
Paso 1 : Establecer R = 0 y Q = 0
Paso 2 : Tomar i = 3 (uno menos que el número de bits en N)
Paso 3 : R = 00 (cambiar 1 a la izquierda)
Paso 4 : R = 01 (poner R (0 ) igual a N(i))
Paso 5 : R < D, omita el comando
Paso 2 : Establecer i = 2
Paso 3 : R = 010
Paso 4 : R = 011
Paso 5 : R < D, omitir comando
Paso 2 : Establecer i = 1
Paso 3 : R = 0110
Paso 4 : R = 0110
Paso 5 : R >= D, se ejecuta el comando
Paso 5b : R = 10 (R−D)
Paso 5c : Q = 10 (establecer Q(i) igual a 1)
Paso 2 : Establecer i = 0
Paso 3 : R = 100
Paso 4 : R = 100
Paso 5 : R >= D, se ejecuta el comando
Paso 5b : R = 0 (R−D)
Paso 5c : Q = 11 (establecer Q(i) igual a 1)
Fin ( ) y .
Todos los métodos de división lenta se basan en la relación de recurrencia estándar [1]
dónde:
La división reconstructiva funciona con números fraccionarios de punto flotante y depende de una suposición .
Los números privados se forman a partir del conjunto .
Algoritmo de recuperación básico para binario (base 2):
R : = N D : = D << n -- R y D deben ser palabras dos veces más largas que N y Q para i : = n − 1 .. 0 do -- Por ejemplo, 31..0 para R de 32 bits : = 2 * R − D -- Resta de prueba del valor desplazado (multiplicar por 2 es un cambio en la interpretación binaria) si R >= 0 entonces q ( i ) : = 1 -- El resultado es el bit 1 sino q ( i ) : = 0 -- El resultado es el bit 0 R : = R + D -- El nuevo resto parcial es igual al valor desplazado (restaurado) end end -- Aquí: N = dividendo, D = divisor, n = número de bits, R = resto parcial, q(i) = bit #i del cocienteLa variante del algoritmo que no implementa lo retiene explícitamente y, por lo tanto , no necesita volver a agregarse en el caso de .
La división sin restauración usa un conjunto de dígitos para los dígitos del cociente en lugar de . El algoritmo es más complejo, pero tiene la ventaja cuando se implementa en chips de que solo hay una decisión y una suma/resta por bit de cociente. No hay un paso de recuperación después de la resta, lo que puede reducir el número de operaciones a la mitad y permite que el algoritmo se ejecute más rápido [2] . Algoritmo de división sin restauración para números positivos binarios (base 2):
R : = N D : = D << n -- R y D deben ser palabras dos veces más largas que N y Q para i = n − 1 .. 0 do -- Por ejemplo, 31..0 para 32 bits si R > = 0 entonces q [ i ] : = + 1 R : = 2 * R − D otra cosa q [ i ] : = − 1 R : = 2 * R + D final si final -- Aquí: N = dividendo, D = divisor, n = número de bits, R = resto parcial, q(i) = bit #i del cociente.Si seguimos este algoritmo, obtenemos el cociente en un formato no estándar, que consta de los números -1 y +1. Este formulario debe convertirse a formato binario. Ejemplo:
Convertir cociente a dígitos : | |
Comienzo: | |
1. Formamos un término positivo: | |
2. Formamos un término negativo: | |
3. Reste: : | |
El término negativo se representa en inversa binaria , no en complemento a dos. |
Si los caracteres −1 se almacenan como ceros (0), como en la representación habitual, entonces el cálculo es trivial: realiza un complemento bit a bit (un bit se reemplaza por el complemento de un bit) del original .
Q : = Q − bit . bnot ( Q ) -- Si los dígitos −1 en Q se representan como ceros.Finalmente, los cocientes calculados por este algoritmo siempre son impares y el resto R está dentro de . Por ejemplo, . Para llevarlo a un resto positivo, tomamos un paso de recuperación después de que se haya reducido de una forma no estándar a una estándar:
si R < 0 entonces Q : = Q − 1 R : = R + D termina siEl verdadero resto es R >> n. Al igual que con la recuperación, los bits menos significativos se usan en el mismo orden en que se forman los bits del cociente , y tiene sentido usar un solo cambio de registro para ambos números al mismo tiempo.
La división lleva el nombre de las primeras letras de los nombres de los creadores (Sweeney, Robertson, Tocher). La división SRT es un método de división popular en muchos microprocesadores [3] [4] . La división es similar a la división sin recuperación, pero usa una tabla de búsqueda basada en el dividendo y el divisor para determinar el cociente.
La diferencia más significativa es que se utiliza una representación redundante para lo privado. Por ejemplo, si se implementa la división en base 4 de SRT, cada dígito del cociente se elige entre cinco posibilidades : . En vista de esto, la elección del dígito del cociente no tiene por qué ser exacta. Más tarde, las cifras del cociente se pueden corregir. Por ejemplo, pares de dígitos y son equivalentes porque . Esta tolerancia le permite seleccionar los dígitos del cociente basándose solo en los pocos bits más significativos del dividendo y el divisor, en lugar de restar por la longitud total. Esta simplificación, a su vez, permite el uso de una base para números mayores de 2.
Al igual que la división sin restauración, los pasos finales son restar la longitud total de los números para obtener el último bit del cociente y convertir el cociente a binario estándar.
El infame error de división flotante en los procesadores Intel Pentium fue causado una tabla de búsqueda codificada incorrectamente. Cinco de las 1066 celdas de la tabla se omitieron por error [5] [6] .
La división de Newton-Rapson usa el método de Newton para encontrar el recíproco de un número y multiplica ese recíproco por para encontrar el cociente resultante .
Pasos de división de Newton-Rapson:
Para aplicar el método de Newton para encontrar el recíproco de un número, necesitas encontrar una función que tenga cero en el punto . Obviamente, esta función es , pero las iteraciones de Newton-Rapson no son exitosas para ella, ya que no se pueden realizar sin conocer el recíproco del número (además, el método intenta calcular el recíproco exacto en un solo paso, y no hacer mejoras iterativas ). La función que funciona es aquella para la que la iteración de Newton-Rapson da
que se puede calcular usando solo una multiplicación y una resta o dos multiplicaciones- sumas .
Desde un punto de vista computacional, las expresiones y no son equivalentes. Para obtener una precisión de 2n bits utilizando la segunda expresión, debe calcular el producto entre y con doble precisión hasta la precisión especificada ( n bits). Por el contrario, el producto debe calcularse solo con una precisión de n bits, ya que los n bits iniciales (después del punto binario) son cero.
Si el error se define como , entonces
Este error cuadrático en cada paso de iteración (la llamada convergencia cuadrática del método de Newton-Rapson) tiene el efecto de que el número de dígitos correctos en el resultado se duplica aproximadamente para cada iteración , una propiedad que se vuelve extremadamente importante cuando los números encontrados tienen muchos dígitos. . Pero también significa que la convergencia inicial del método puede ser relativamente lenta, especialmente si el valor se elige mal.
Para el subproblema de elegir una estimación inicial, es conveniente aplicar un desplazamiento al divisor para que quede entre , mientras que al dividendo se le aplica el mismo desplazamiento para que el cociente no cambie. Entonces uno puede usar la Aproximación Lineal en la forma
para inicializar el método de Newton-Rapson. Para minimizar el error absoluto máximo de esta aproximación en el intervalo , se debe usar
Los coeficientes de aproximación lineal se definen como sigue. El valor absoluto del error es . El mínimo del valor absoluto máximo del error se determina de acuerdo con el teorema de oscilación equivalente de Chebyshev aplicado a . El mínimo local de la función estará en el punto donde , que tiene solución . La función en este mínimo debe tener un valor con signo opuesto con respecto a los puntos extremos del intervalo, a saber, . Dos igualdades en dos incógnitas dan una única solución y , y el error máximo es . Al usar esta aproximación, el valor absoluto del error del valor inicial es menor que
Es posible formar un polinomio de grado superior a 1 calculando los coeficientes según el algoritmo de Remez . Luego, la aproximación inicial requiere más cálculo, lo que puede compensarse con un número menor de iteraciones de Newton-Rapson.
Dado que la convergencia de este método es exactamente cuadrática, se sigue que es suficiente
pasos para calcular el valor hasta lugares binarios. Esto es equivalente a 3 para IEEE sencillos y 4 para dobles y dobles extendidos .
PseudocódigoEl siguiente pseudocódigo calcula el cociente de N y D , hasta P dígitos binarios:
Expresar D como donde (representación de punto flotante estándar) // convertir a un valor entre 0,5 y 1, lo que se puede hacer desplazando bits / restando el exponente // constantes precalculadas con la misma precisión que los tiempos de repetición de D // se pueden calcular en anticipo para una P fija devolución finalPor ejemplo, para la división de punto flotante de doble precisión, este método utiliza 10 multiplicaciones, 9 sumas y 2 desplazamientos.
Variantes de la división de Newton-RapsonEl método de división de Newton-Rapson se puede modificar para que sea un poco más rápido. Después de cambiar N y D para que D esté en el intervalo [0.5, 1.0], inicializamos con
.Esta es la mejor aproximación cuadrática y da un valor de error absoluto de como máximo . Los parámetros se eligen de forma que se elija un error igual al valor del tercer orden del polinomio de Chebyshev de primera especie. Los coeficientes deben calcularse previamente y codificarse en el método.
Luego, en el ciclo, usamos una iteración que eleva el error a un cubo.
Si el bucle se ejecuta hasta que se acerca a los bits iniciales, entonces el número de iteraciones será como máximo
que es igual al número de veces 99 al cubo para obtener . Después
es igual al cociente hasta bits.
El uso de polinomios de mayor grado en inicializaciones o iteraciones da como resultado un impacto en el rendimiento, ya que sería mejor utilizar multiplicaciones adicionales para hacer más iteraciones.
La división de Goldschmidt [7] (Robert Elliott Goldschmidt) usa un proceso iterativo de multiplicar repetidamente el dividendo y el divisor por el mismo factor , elegido para que el divisor converja a 1. Esto hace que el dividendo converja al cociente :
Pasos de la división de Goldschmidt:
Suponga escalado al valor , cada uno basado en :
Multiplicamos el dividendo y el divisor por el factor y obtenemos:
Después de un número suficiente de iteraciones .
El método Goldschmidt se utiliza en los procesadores AMD Athlon y posteriores [8] [9] . También se conoce como el algoritmo Anderson Earle Goldschmidt Powers (AEGP) y se implementa en varios procesadores de IBM [10] [11] . Aunque la convergencia del método es la misma que la de la implementación de Newton-Rapmon, una de las ventajas del método de Goldschmidt es que las multiplicaciones en el numerador y el denominador se pueden realizar en paralelo [11] .
Teorema del binomioEl método de Goldschmidt se puede utilizar con factores que permitan la simplificación con el binomio de Newton . Supongamos que N/D se multiplica por una potencia de dos de modo que . Elegimos y . Esto da
.Después de los pasos , el denominador se puede redondear con un error relativo
,que tiene un valor máximo en , proporcionando una precisión mínima en dígitos binarios.
Los métodos destinados a ser implementados en hardware no suelen contar con números enteros con miles o millones de dígitos decimales, lo cual es común, por ejemplo, en cálculos de módulo en criptografía . Para estos números grandes, los algoritmos más eficientes convierten el problema en el uso de una pequeña cantidad de multiplicaciones, lo que se puede hacer con algoritmos de multiplicación asintóticamente eficientes , como el algoritmo de Karatsuba , la multiplicación de Tum-Cook o el algoritmo de Schönhage-Strassen . Como resultado , la complejidad computacional de la división será del mismo orden (hasta la multiplicación por una constante) que la multiplicación. Los ejemplos incluyen la reducción a la multiplicación de Newton como se describe anteriormente [12] , así como los algoritmos ligeramente más rápidos de la división de Bournickel-Ziegler [ 13] , Baret y Montgomery [14] . El método de Newton, en particular, es efectivo en escenarios donde se deben realizar múltiples divisiones por un número determinado, ya que después de encontrar inicialmente el recíproco, solo se necesita una multiplicación (reducida) para cada multiplicación.
Dividir por una constante es equivalente a multiplicar por su recíproco . Como el denominador es constante, el recíproco también es constante . Luego puede calcular el valor una vez y durante los cálculos realizamos la multiplicación en lugar de la división . En la aritmética de coma flotante, el uso causa un pequeño problema relacionado con la optimización del código por parte de los compiladores [a] , pero en la aritmética de enteros , el resto siempre será cero, siempre que .
No es necesario usar exactamente , puede usar cualquier valor que se reduzca a . Por ejemplo, al dividir por 3 , puede usar fracciones , o . Por lo tanto, cuando es una potencia de dos, el paso de división se puede reducir a un desplazamiento rápido a la derecha. Como efecto, el cálculo de cómo reemplaza la división con la multiplicación y el desplazamiento. Tenga en cuenta que es precisamente este procedimiento el que es esencial aquí, ya que dará como resultado cero.
Sin embargo, si ya es una potencia de dos, no existen y se cumplen las condiciones anteriores. Afortunadamente, da exactamente el mismo resultado en aritmética de enteros que , incluso si no es exactamente igual a , pero "lo suficientemente cerca", de modo que el error introducido por la aproximación está en los bits que desaparecerán después de la operación de desplazamiento [15] [ 16 ] [17] [segundo]
Como ejemplo específico de aritmética de punto fijo , para enteros sin signo de 32 bits, la división por 3 se puede reemplazar por multiplicar por , es decir, multiplicar por 2863311531 ( hexadecimal 0xAAAAAAAB) seguido de un desplazamiento a la derecha de 33 bits. El valor 2863311531 se calcula como , luego se redondea hacia arriba. De manera similar, una división por 10 se puede expresar como una multiplicación por 3435973837 (0xCCCCCCCD) seguida de una división por (o un desplazamiento a la derecha de 35 bits) [19] . OEIS da una secuencia de constantes para la multiplicación como A346495 y para el desplazamiento a la derecha como A346496 .
Para una división de enteros sin signo de bits comunes donde el divisor no es una potencia de dos, la siguiente identidad transforma la división en sumas/restas de dos bits, una multiplicación de números de bit por bit (donde solo la mitad superior del resultado se utiliza), y varios turnos, precálculo y :
dónde
En algunos casos, la división por una constante se puede hacer incluso en menos tiempo reemplazando la "multiplicación por una constante" con una serie de cambios y sumas o restas [20] . De particular interés es la división por 10, para la cual se obtiene el cociente exacto con un resto si es necesario [21] .
Algoritmos de teoría de números | |
---|---|
Pruebas de simplicidad | |
Encontrar números primos | |
Factorización | |
logaritmo discreto | |
Encontrar MCD | |
Módulo aritmético | |
Multiplicación y división de números. | |
Calcular la raíz cuadrada |