Um algoritmo para dividir inteiros positivos Walter Mascarenhas Nota de aula de MAC0122 16 de setembro de 2004 Nesta nota eu apresento um algoritmo para dividir um número n da forma a22k + b por um número d, onde a, b e c são inteiros não negativos e menores que 22k . Este algoritmo não tenta ser o “melhor” nem o mais o eficiente; ele só ilustra idéias. Se você quiser algo feito com detalhes então procure Internet. Veja por exemplo o site http://www.swox.com/gmp. O primeiro passo para se fazer um algoritmo para divisão é entender o que é divisão. As pessoas esquecem a definição de divisão e pensam no quociente e no resto separadamente. Na verdade, o quociente e o resto são definidos juntos: dividir n por d é encontrar, simultaneamente, um par de números q e r tais que n = qd + r, com 0 ≤ r < |d|. (1) Esta é a definição matemática de divisão. Porém esta NÃO É a forma como os números inteiros com sinal são usualmente divididos no computador. Felizmente, a matemática e a computação concordam quando n e d são positivos, e como o nosso algoritmo só lida com números positivos nós não vamos nos preocupar com isso. Para nós, dividir n por d significará fazer a decomposição de n nas duas partes descritas na equação (1). A idéia do nosso algoritmo para dividir a22k + b por d é quebrar b e d em números menores, ou seja, fazer b = 2k b0 + b1 e d = 2k d0 + d1 , com 0 ≤ bi , di < 2k . Por exemplo, no caso que estudamos na aula a base é 232 e farı́amos b = 216 b0 +b1 . Com isto nós podemos multiplicar os bi e di sem risco de overflow (assumimos que o nosso computador lida com números até 22k − 1 de forma exata.) 1 Com aquecimento, na próxima seção nós analisamos este caso particular no qual d0 = 0. Este é o caso mais simples, que você aprendeu a resolver no primário. Na seção seguinte tratamos o caso d0 > 0. Para isso, estudaremos um problema auxiliar mais simples. Este problema auxiliar é discutido nas duas última seção desta nota, que completa a apresentação do algoritmo de divisão. 1 O caso d0 = 0 No caso d0 = 0 temos que d é menor que 2k e portanto o resto também deve ser menor que 2k . Com isto, se fizermos q = q0 22k + q1 2k + q2 , com q0 < 22k e q1 , q2 < 2k escrevermos a equação (1) por extenso obteremos n = a22k + b0 2k + b1 = d1 q0 22k + d1 q1 2k + d1 q2 + r1 . (2) Aplicamos então o mesmo algoritmo que usamos para dividir quando estamos trabalhando na base 10. Este algoritmo pode ser expresso assim: 1. Faça a divisão a = d1 q0 + x para obter q0 . Substituindo em (2) reduzimos o problema à equação a22k + b0 2k + b1 = (d1 q0 + x)22k + b0 2k + b1 = d1 q0 22k + d1 q1 2k + d1 q2 + r1 , que com o cancelamento do termo d1 q0 22k leva a x22k + b0 2k + b1 = d1 q1 2k + d1 q2 + r1 . (3) 2. Faça a divisão x2k + b0 = d1 q1 + y para obter q1 . Substituindo em (2) obtemos (x2k + b0 )2k + b1 = (d1 q1 + y)2k + b1 = d1 q1 2k + d1 q2 + r1 , que com o cancelamento do termo d1 q1 2k leva a y2k + b1 = d1 q2 + r1 . 3. Finalmente, faça a divisão y2k + b1 = d1 q2 + r1 para obter q2 e r1 . Pronto, calculamos q = q0 22k + q1 2k + q2 e r = r0 2k + r1 . 2 (4) 2 O caso d0 > 0 Este caso é mais complexo. Para lidar com ele resolveremos o problema auxiliar: Como dividir x = y2k + z por d = d0 2k + d1 se y < 22k , z < 2k e d0 > 0? (5) Este problema auxiliar é mais simples, pois podemos representar q com apenas um dı́gito. De fato, note que q=b y2k + z (22k − 1)2k + 2k − 1 23k − 1 c≤b c ≤ b c = 22k − 1. k k d d0 2 + d1 2 Logo q pode ser representado em um único dı́gito. Na próxima seção eu explicarei como resolver este problema auxiliar. Vamos assumir então que já sabemos fazer a divisão y2k + z = qd + r, quando y < 22k , z < 2k e d0 > 0. O algoritmo para d0 fica assim então: 1. Usando o algoritmo da próxima seção, faça a2k + b0 = qa d + ra . Com isto, n passa a ser dado pela expressão n = (qa d + ra )2k + b1 = qa 2k d + ra 2k + b1 = qd + r. 2. Use novamente o algoritmo da próxima seção para obter ra 2k + b1 = qb d + r Isto leva a n = (qa 2k + qb )d + r. 3. Finalmente, faça a multiplicação e a soma qa 2k + qb para obter q. Assim completamos a divisão para d0 > 0. Agora só resta analisar a solução do problema (5) na próxima seção. 3 3 O problema auxiliar Nesta seção eu explico como resolver o problema auxiliar (5): Como dividir x = y2k + z por d = d0 2k + d1 se y < 22k , z < 2k e d0 > 0? A idéia do algoritmo é simples: ao invés de dividir sempre por d = d0 2k + d1 , faça uma divisão por d0 + 1 na hora certa. Aqui está o algoritmo: 1. Faça a divisão y = qy d + ry , obtendo um resto ry < d. 2. O passo anterior reduz o problema a decompor x = qy d2k + ry 2k + z. Agora faça a divisão ry = q1 (d0 + 1) + r1 . Observe que r1 < d0 + 1 implica r1 < 2k e ry d d0 2k + d1 d0 2k + 2k q1 ≤ < = < = 2k . d0 + 1 d0 + 1 d0 + 1 d0 + 1 Logo q1 (2k − d1 ) < 22k e r1 2k + z < 22k e podemos computar os termos da equação abaixo sem problemas: x = qy d2k + (q1 (d0 + 1) + r1 )2k + z = qy d2k + q1 (d0 2k + d1 ) + r1 2k + q1 (2k − d1 ) + z = q2 d + q1 (2k − d1 ) + (r1 2k + z), para q2 = qy 2k + q1 . 3. Agora faça as divisões r1 2k + z = q2 d + r2 , q1 (2k − d1 ) = q3 d + r3 , 4 que levam a x = q4 d + r1 + r2 . para q4 = q1 + q2 + q3 . Se r1 ≥ d − r2 então faça x = qd + r para r = r1 − (d − r2 ) < d e q = q4 + 1. Caso contrário, faça x = qd + r. para q = r1 + r2 < d e q = q4 . Como sabemos que q < 22k não precisamos nos preocupar com overflow ao avaliarmos os q’s. É isto, este é o algoritmo. 5