Multiplicação de números grandes

Quanto tempo é necessário para multiplicar dois números naturais? Se os números têm m e n dígitos respectivamente, o algoritmo usual consome tempo proporcional a m × n.

×1414213562373095048801688724209
×2223141592653589793238462643383
222222222222222222222222222222???

É difícil imaginar um algoritmo mais rápido que o usual se m e n forem pequenos (algumas centenas, digamos). Mas existe um algoritmo que é bem mais rápido quando m e n são grandes (como acontece em criptografia, por exemplo).

O algoritmo de Karatsuba (Карацуба) usa o método de divisão e conquista. A análise do desempenho do algoritmo leva a uma interessante recorrência.

Esta página ensina algumas lições de caráter geral: (1) para muitos problemas, existem algoritmos surpreendentes mais rápidos que o algoritmo usual; (2) às vezes, a superioridade do algoritmo mais sofisticado só se manifesta nas instâncias muito grandes do problema; (3) as técnicas de resolução de recorrências são capazes de fazer uma análise muito precisa do desempenho de algoritmos recursivos.

O problema

O problema da multiplicação de números naturais consiste no seguinte: Dados números naturais u e v, calcular o produto u × v.

Usaremos notação decimal para representar números naturais. Nessa notação, um dígito é qualquer número natural no intervalo 0 .. 9. Você deve imaginar que cada número natural é representado por um vetor de dígitos decimais. Esse vetor é a expansão decimal do número. Mas nossa discussão será conduzida num nível de abstração alto, sem manipulação explícita desse vetor.

Ao discutir o problema, é conveniente explicitar o tamanho dos números a multiplicar. Diremos que um número natural u tem no máximo n dígitos se u < 10n. Assim, o problema passa a ter a seguinte forma:

Problema da multiplicação:  Dados números naturais u e v com no máximo n dígitos cada, calcular a expansão decimal do produto u × v.

O algoritmo usual de multiplicação resolve o problema em tempo proporcional a n².  Existe algoritmo mais rápido?

Exemplo A:  Veja o rastreamento da execução do algoritmo usual ao multiplicar 9999 por 7777:

22229999
22227777
22269993
2269993
269993
69993
77762223

Exercícios 1

  1. Suponha que os números naturais u e v têm no máximo n dígitos decimais cada. Quantos dígitos tem, no mínimo e no máximo, o número u × v? Quantos dígitos tem, no mínimo e no máximo, o número u + v?
  2. Escreva, em código, o algoritmo usual de multiplicação de dois números naturais. O algoritmo deve receber um vetor de dígitos U[1 .. n] que representa um número natural u e um vetor de dígitos V[1 .. n] que representa um número natural v e deve devolver um vetor UV de dígitos que representa o produto u×v.
  3. Escreva, em código, o algoritmo usual de soma de dois números naturais. O algoritmo deve receber um vetor de dígitos U[1 .. n] que representa um número natural u e um vetor de dígitos V[1 .. n] que representa um número natural v e deve devolver um vetor UplusV de dígitos que representa a soma u + v.

Divisão e conquista

Sejam u e v dois números com no máximo n dígitos cada. Suponha, por enquanto, que n é par. Seja p o número formado pelos n/2 primeiros dígitos (dígitos mais significativos) de u e seja q o número formado pelos n/2 últimos dígitos (dígitos menos significativos) de u. Então

u  =  p × 10n/2 + q.

Defina r e s analogamente para v, de modo que v = r × 10n/2 + s. Teremos então

  u × v  =  p × r × 10n + (p × s + q × r) × 10n/2 + q × s.  (A)

O lado direito da equação tem apenas 4 multiplicações verdadeiras (a saber, p por r, p por s, q por r e q por s). A multiplicação por 10n não conta como multiplicação porque representa um mero deslocamento (= shift) do vetor todo para a esquerda. Esse deslocamento é muito mais barato que uma multiplicação pois consome meras n unidades de tempo. Observação análoga vale para a multiplicação por 10n/2.

A expressão (A) reduz a multiplicação de dois números com no máximo n dígitos cada a quatro multiplicações de números com no máximo n/2 dígitos cada. Essa é a fase de divisão do método de divisão e conquista; as fases de conquista e combinação serão discutidas nas seções seguintes.

p × r p × s
q × r q × s

Infelizmente, essa redução de 1 multiplicação grande para 4 pequenas não basta, por si só, para tornar a multiplicação mais eficiente que a usual, como mostra um dos exercícios abaixo.

Exemplo B:  Quero multiplicar 6514202 por 9898989. O resultado das contas pode ser resumido assim:

22222226514202__u = p × 104 + q
22222229898989__v = r × 104 + s

64383929898989__p × r
22258518392222__p × s
22241557782222__q × r
22222237771778__q × s

64484013941778__p × r × 108 + (p × s + q × r) × 104 + q × s

Exercícios 2

  1. A janelinha da minha calculadora comporta no máximo 8 dígitos decimais. Como fazer para multiplicar 6514202 por 9898989?
  2. ★ Escreva um algoritmo recursivo de multiplicação baseado na fórmula (A). O algoritmo deve multiplicar um número natural u por um número natural v com no máximo n dígitos cada. Escreva a recorrência que dá o consumo de tempo do algoritmo. Resolva a recorrência. A qual classe Θ a solução pertence?

O algoritmo de Karatsuba

Felizmente, os três números de que precisamos do lado direito de (A) — a saber p × r, (p × s + q × r) e q × s — podem ser obtidos com apenas três multiplicações. Com efeito, se adotarmos uma nova variável y definida por

y = (p + q) × (r + s),

teremos p × s + q × r  =  yp × rq × s e portanto a equação (A) pode ser reescrita assim:

  u × v = p × r × 10n + ( y − p × r − q × s) × 10n/2 + q × s.  (B)

É bem verdade que esta equação envolve duas adições e duas subtrações adicionais, mas essas operações consomem muito menos tempo que as multiplicações. Por isso, (B) vale a pena.

Se n não for par, basta trocar n/2 por n/2⌉ , o que equivale a acrescentar um 0 à esquerda de u e de v. Se denotarmos n/2⌉ por m, teremos  u = p × 10m + q  e  v = r × 10m + s  e portanto

u × v  =  p × r × 102m + ( y − p × r − q × s) × 10m + q × s,

Exemplo C:  Quero multiplicar 6514202 por 9898989. O resultado das contas pode ser resumido assim:

22222206514202__u = p × 104 + q
22222209898989__v = r × 104 + s

64383929898989__p × r

22222248532222__p + q
22222299782222__r + s
22484232342222__y = (p + q) × (r + s)
22100076172222__z = y − p × r − q × s

22222237771778__q × s

64484013941778__p × r × 108 + z × 104 + q × s

As seguintes observações não afetam o exemplo, mas são relevantes para entender o código que virá a seguir: Se u e v tivessem n dígitos cada, p × r e q × s teriam n+1 dígitos cada, y teria n+2 (mais precisamente, só n+1) dígitos e u × v teria 2n dígitos.

Essas ideias são a base do algoritmo de Karatsuba e Ofman. O algoritmo recebe números naturais u e v com no máximo n dígitos cada e devolve o produto u × v:

Karatsuba (u, v, n)
11 . se n ≤ 3
12 .ooo devolva u × v e pare
13 . m := ⌈n/2⌉
14 . p := ⌊u/10m
15 . q := u mod 10m
16 . r := ⌊v/10m
17 . s := v mod 10m
18 . pr := Karatsuba (p, r, m)
19 . qs := Karatsuba (q, s, m)
10 . y := Karatsuba (p + q, r + s, m+1)
11 . uv := pr × 102m + (yprqs) × 10m + qs
12 . devolva uv

(Observe que a variável pr armazena o produto p × r, a variável qs armazena o produto q × s, e assim por diante.) Na linha 7, v mod 10m é o resto da divisão de v por 10m, ou seja, o número representado pelos últimos m dígitos da expansão decimal de v. Na terminologia do método de divisão e conquista, as linhas 3 a 7 constituem a fase de conquista do algoritmo.

As instâncias em que n vale 1, 2 ou 3 devem ser tratadas na base da recursão porque o algoritmo é aplicado, na linha 10, a uma instância de tamanho n/2⌉ +1, e este número só é menor que n quando n > 3.

É claro que o algoritmo produz o resultado correto se n ≤ 3. Suponha agora que n > 3. Como m < n, podemos supor, por hipótese de indução, que a linha 8 produz o resultado correto, ou seja, que pr = p × r. Analogamente, podemos supor que qs = q × s, ps = p × s e qr = q × r no início da linha 11. Isso encerra a fase de conquista do algoritmo. A linha 11 não faz mais que implementar (B), que constitui a fase de combinação do algoritmo. Portanto, uv = u × v.

Exercícios 3

  1. O algoritmo de Karatsuba usa o método de divisão e conquista para calcular o produto de dois números naturais uv. No que consiste a fase de divisão do algoritmo de Karatsuba? No que consiste a fase de conquista? No que consiste a fase de combinação? Dê respostas breves e informais.
  2. Uma certa aplicação envolve multiplicação de números cujo comprimento (ou seja, número de dígitos) é uma potência inteira de 2. Queremos escrever uma versão do algoritmo de Karatsuba sob medida para essa aplicação. O código é mais simples que o da versão genérica do algoritmo?
  3. Programação.  Implemente o algoritmo de Karatsuba. Implemente o algoritmo usual de multiplicação. (Use linguagem C. Não use C++ nem Java.)  Faça testes para comparar a velocidade dos dois algoritmos.

Desempenho do algoritmo de Karatsuba

Quanto tempo o algoritmo Karatsuba consome? É razoável supor que o consumo de tempo do algoritmo só depende do número de dígitos, n, de u e de v. (Portanto, não há um melhor caso nem um pior caso.) Denote por T(n) o consumo de tempo do algoritmo. Podemos supor que as linhas 1 a 7 do algoritmo consomem n unidades de tempo. Então,

  T(n) = 2 T(⌈n/2⌉) + T(⌈n/2⌉ +1) + n  (C)

para n = 4, 5, 6, etc.  As duas parcelas T(⌈n/2⌉) se referem às linhas 8 e 9. A parcela T(⌈n/2⌉ +1) se refere à linha 10. A parcela n resume o consumo de tempo das demais linhas.

Veja o detalhamento da parcela n da recorrência. As linhas 1 e 2 consomem 1 uma unidade de tempo cada. A linha 3 consome 1 uma unidade de tempo pois pode ser implementada com um shift de 1 bit da representação binária de n. A linha 4 consome n/2 unidades de tempo, pois consiste em um mero shift do vetor que representa u. Por razões análogas, as linha 5 a 7 consomem n/2 unidades de tempo cada. Na linha 10, o cálculo do argumento p + q da função consome n/2 unidades de tempo. Da mesma forma, o cálculo do argumento r + s da função consome n/2 unidades de tempo. A linha 11 envolve quatro somas/subtrações e dois shifts, e portanto consome 6n unidades de tempo. Resumindo: o conjunto de todas as operações que não envolvem chamadas recursivas consome 9n + 2 unidades de tempo. Como estamos interessados no consumo de tempo assintótico, podemos nos atrever a escrever n no lugar de 9n + 2. Isso explica o termo n da recorrência.

Qual a solução da recorrência (C)? Poderíamos apelar para o Teorema Mestre e concluir que T(n) = Θ(nlg 3). Mas é mais interessante deduzir isso diretamente. Para começar, podemos estudar uma recorrência mais simples, na esperança de que ela tenha uma solução semelhante:

S(n) = 3 S(n/2) + n

para n = 2¹, 2², 2³, …  Essa recorrência pode ser desenrolada da seguinte maneira:

S(n) = 3 S(n/2) + n
  = 3 (3 S(n/4) + n/2) + n
  = 32S(n/22) + 3 n/2 + n
  = 33S(n/23) + 32n/22 + 31n/21 + n
  = 3iS(n/2i) + 3i−1n/2i−1 + ⋅⋅⋅ + 31n/21 + n

Quando i atinge lg n, temos S(n/2i) = S(1) = 1 = n/2i. Logo,

S(n) = 3in/2i + 3i−1n/2i−1 + ⋅⋅⋅ + 31n/21 + 30n/20
  = n ((3/2)i + (3/2)i−1 + ⋅⋅⋅ + (3/2)1 + (3/2)0)
  = n((3/2)i+1 − 1) / (3/2 − 1)
  = 2 n ((3/2)i+1 − 1)
  = 2i+1 (3/2)i+1  −  2i+1
  = 3i+1  −  2i+1
  = 3 × 3i  −  2 × 2i
  = 3 × 3lg n  −  2 × 2lg n
  = 3n lg 3  −  2n .

A solução S da versão simplificada da recorrência (C) está, portanto, em Θ(nlg 3). A experiência mostra (e o Torema Mestre confirma) que a solução T da recorrência (C) original também está em

Θ(nlg 3).

Um dos exercício abaixo acaba com as dúvidas que possam ter sobrado.

O número lg 3 fica entre 1.5 e 1.6 e portanto n lg 3 fica entre n √n e n2.  Assim, o algoritmo Karatsuba é assintoticamente mais rápido que o algoritmo quadrático usual de multiplicação. Mas, em virtude da constante multiplicativa escondida sob a notação Θ, essa superioridade só se manifesta quando n é maior que algumas centenas.

Exercícios 4

  1. Calcule T(4), … , T(8) a partir da recorrência (C) supondo T(2) = 2 e T(3) = 3.
  2. ★ Suponha que o algoritmo de Karatsuba leva 1 minuto para multiplicar dois números de n dígitos cada. Quanto tempo levará para multiplicar dois números de 10n dígitos cada?  Faça uma estimativa análoga para o algoritmo de multiplicação usual.
  3. ★ Quantas unidades de tempo o algoritmo Karatsuba consome para multiplicar dois números com 1000 dígitos cada?
  4. ★ Seja T a função definida assim:  T(1) = 1 e T(n) = 3 T(⌈n/2⌉) + n para n = 2, 3, 4, 5, …  Prove (por indução em n) que T(n) = Ο(nlg 3). Prove que T(n) = Ω(nlg 3).
  5. Seja T a solução da recorrência (C). Prove, por indução em n, que T é estritamente crescente, ou seja, que T(n) < T(n +T(n)1) para todo n natural.
  6. ★ Seja T a solução da recorrência (C) com valores iniciais T(2) = 2 e T(3) = 3. Prove que T(n) = Ω(nlg 3). Prove que T(n) = Ο(nlg 3).
  7. Qual das seguintes afirmações está correta:  nlg 3 = Θ(n1.58)  ou  nlg 3 = Θ(n1.59) ?