Algorytm mnożenia n-bitowych liczb naturalnych x⋅y
Możemy przedstawić liczby x i y jako ciągi binarne, które potem możemy podzielić na pół tak, że x=22nxL+xRy=22nyL+yR
Przykładowo: x=(1011 0110)2, wówczas xL=(1011)2, xR=(0110)2
Następnie zauważmy, że x⋅y=2nxLyL+22n(xLyR+xRyL)+xRyR.
Niestety algorytm typu Divide & Conquer wykorzystujący powyższy podział i połączenie pod-problemów nie ma lepszej złożoności obliczeniowej od standardowego mnożenia, czyli O(n2). Natomiast jeśli zauważymy, że xLyR=(xL+xR)(yL+yR)−xLyL−xRyR wtedy dostajemy trzy 2n-bitowe (a nie cztery jak wcześniej) pod-problemy:
xLyL, xRyR, (xL+xR)(yL+yR) co skutkuje złożonością O(nlog23), gdzie log23≈1.59.
Złożoność obliczeniowa
Zamiast T(n)=4⋅T(2n)+O(n) mamy T(n)=3⋅T(2n)+O(n) co daje nam złożoność jedynie O(n1.59).
Całkowity czas spędzony na poziomie k w drzewie rekursji wynosi 3k⋅O(2kn)=(23)k⋅O(n). ponieważ każdy pod-problem k tworzy trzy pod-pod-problemy o rozmiarze 2kn.
Warto zauważyć, że O(3log2n)=O(nlog23), ponieważ 3log2n=(nlogn3)log2n=nlogn3 ⋅ log2n=nlog2(nlogn3)=nlog23.
Pseudokod:
function multiply(
x,y)
:
input: x,y∈N w kodzie binarnym o długości n
output: multiply(
x,y)
=x⋅y
- n=max(size of x, size of y)
if
n=1:
- xL,xR=
leftmost
⌈2n⌉, rightmost
⌊2n⌋ bits of x
- yL,yR=
leftmost
⌈2n⌉, rightmost
⌊2n⌋ bits of y
- P1=
multiply(
xL,yL)
- P2=
multiply(
xR,yR)
- P3=
multiply(
xL+xR,yL+yR)
return
P1⋅2n+(P3−P1−P2)⋅22n+P2
Source: Algorithms: Dasgupta, Papadimitriou, Vazirani - paragraph 2.1