Karatsuba
Karatsuba算法是一种用于大整数乘法运算的高效算法,由Anatoly Karatsuba在1960年提出,它基于分治策略显著降低了大整数乘法的时间复杂度,以下是对该算法详细的介绍:
一、算法背景
在处理大整数乘法时,传统的竖式乘法计算复杂度较高,时间复杂度为 O(n2)O(n^2)O(n2)(其中 nnn 是整数的位数)。例如,两个1000位的整数相乘,按照传统方法需要进行大约 10002=10000001000^2 = 100000010002=1000000 次基本乘法运算,随着整数位数的不断增加,计算量会迅速膨胀。为了更高效地解决大整数乘法问题,Karatsuba算法应运而生,它通过巧妙的数学变换将复杂度降低到了 O(nlog23)≈O(n1.585)O(n^{\log_2 3}) \approx O(n^{1.585})O(nlog23)≈O(n1.585),极大地提高了计算效率。
二、核心原理
Karatsuba算法的核心思想是分治(Divide and Conquer),通过减少乘法运算的次数来降低整体的时间复杂度,它将两个大整数的乘法运算转化为多个较小整数乘法运算的组合,关键在于把原本需要4次乘法的计算通过数学变换减少为3次乘法。
假设要计算两个大整数 AAA 和 BBB 的乘积,设 AAA 和 BBB 的位数为 nnn,将它们进行如下拆分(以十进制为例,二进制等其他进制同理,只是基数不同):
令 m=⌊n/2⌋m = \lfloor n/2 \rfloorm=⌊n/2⌋(向下取整),把 AAA 拆分成 A=A1×10m+A0A = A_1 \times 10^m + A_0A=A1×10m+A0,其中 A1A_1A1 是 AAA 的高 mmm 位部分,A0A_0A0 是 AAA 的低 mmm 位部分;把 BBB 拆分成 B=B1×10m+B0B = B_1 \times 10^m + B_0B=B1×10m+B0,其中 B1B_1B1 是 BBB 的高 mmm 位部分,B0B_0B0 是 BBB 的低 mmm 位部分。
传统方式需要计算4次乘法,即 A1B1A_1B_1A1B1、A1B0A_1B_0A1B0、A0B1A_0B_1A0B1、A0B0A_0B_0A0B0。而Karatsuba算法利用数学技巧,将中间项 A1B0+A0B1A_1B_0 + A_0B_1A1B0+A0B1 进行变换:
A1B0+A0B1=(A1+A0)×(B1+B0)−A1B1−A0B0A_{1}B_{0} + A_{0}B_{1} = (A_{1} + A_{0}) \times (B_{1} + B_{0}) - A_{1}B_{1} - A_{0}B_{0}A1B0+A0B1=(A1+A0)×(B1+B0)−A1B1−A0B0
这样,只需要计算3次乘法,分别是:
- z2=A1×B1z_2 = A_1 \times B_1z2=A1×B1
- z0=A0×B0z_0 = A_0 \times B_0z0=A0×B0
- z1=(A1+A0)×(B1+B0)z_1 = (A_1 + A_0) \times (B_1 + B_0)z1=(A1+A0)×(B1+B0)
最后再通过以下公式合并得到 A×BA \times BA×B 的结果:
A×B=z2×102m+(z1−z2−z0)×10m+z0A \times B = z_2 \times 10^{2m} + (z_1 - z_2 - z_0) \times 10^m + z_0A×B=z2×102m+(z1−z2−z0)×10m+z0
三、算法步骤
-
基础情况判断(递归终止条件):
当要相乘的两个整数位数足够小(通常是小于某个预设的较小阈值,比如在实际应用中可以设定为当整数位数小于等于4位或者8位等,根据具体性能和精度需求决定)时,直接使用普通的竖式乘法来计算它们的乘积,因为对于较小的整数,传统乘法的计算开销相对较小,且避免了过多的递归调用等额外开销。 -
整数拆分:
对于较大的整数 AAA 和 BBB,按照上述核心原理中提到的方式,根据整数当前的位数确定合适的 mmm 值(一般是取位数的一半左右,向下取整确保划分合理),将 AAA 拆分成 A1A_1A1 和 A0A_0A0,将 BBB 拆分成 B1B_1B1 和 B0B_0B0。 -
递归计算3个子乘积:
- 计算 z2=Karatsuba(A1,B1)z_2 = \text{Karatsuba}(A_1, B_1)z2=Karatsuba(A1,B1),即递归调用Karatsuba算法来计算 A1A_1A1 和 B1B_1B1 的乘积,因为 A1A_1A1 和 B1B_1B1 通常也是较大的整数,需要继续分治处理。
- 计算 z0=Karatsuba(A0,B0)z_0 = \text{Karatsuba}(A_0, B_0)z0=Karatsuba(A0,B0),同样递归计算 A0A_0A0 和 B0B_0B0 的乘积。
- 计算 z1=Karatsuba((A1+A0),(B1+B0))z_1 = \text{Karatsuba}((A_1 + A_0), (B_1 + B_0))z1=Karatsuba((A1+A0),(B1+B0)),先计算 A1+A0A_1 + A_0A1+A0 和 B1+B0B_1 + B_0B1+B0 的和,然后再递归调用Karatsuba算法计算它们的乘积。
-
合并结果:
按照公式 A×B=z2×102m+(z1−z2−z0)×10m+z0A \times B = z_2 \times 10^{2m} + (z_1 - z_2 - z_0) \times 10^m + z_0A×B=z2×102m+(z1−z2−z0)×10m+z0,将计算得到的 z2z_2z2、z1z_1z1、z0z_0z0 以及对应的 101010 的幂次进行运算,合并得到最终 AAA 和 BBB 的乘积结果。在这个过程中,可能涉及到进位等处理,要按照相应的进制规则(如十进制满十进一、二进制满二进一)正确处理数字的进位情况,以保证结果的准确性。
四、实例基础
以下以两个十进制整数 A=1234A = 1234A=1234,B=5678B = 5678B=5678 为例,展示Karatsuba算法的计算过程(这里为了方便演示,取 m=2m = 2m=2,即将整数拆分成2位一组):
-
拆分整数:
- A=12×102+34A = 12 \times 10^2 + 34A=12×102+34,所以 A1=12A_1 = 12A1=12,A0=34A_0 = 34A0=34。
- B=56×102+78B = 56 \times 10^2 + 78B=56×102+78,所以 B1=56B_1 = 56B1=56,B0=78B_0 = 78B0=78。
-
计算3个子乘积:
- 计算 z2=A1×B1=12×56z_2 = A_1 \times B_1 = 12 \times 56z2=A1×B1=12×56:
使用传统乘法可得 12×56=67212 \times 56 = 67212×56=672。 - 计算 z0=A0×B0=34×78z_0 = A_0 \times B_0 = 34 \times 78z0=A0×B0=34×78:
通过传统乘法计算得出 34×78=265234 \times 78 = 265234×78=2652。 - 计算 z1=(A1+A0)×(B1+B0)=(12+34)×(56+78)z_1 = (A_1 + A_0) \times (B_1 + B_0) = (12 + 34) \times (56 + 78)z1=(A1+A0)×(B1+B0)=(12+34)×(56+78):
先算括号内的加法,12+34=4612 + 34 = 4612+34=46,56+78=13456 + 78 = 13456+78=134,然后计算乘积 46×134=616446 \times 134 = 616446×134=6164。
- 计算 z2=A1×B1=12×56z_2 = A_1 \times B_1 = 12 \times 56z2=A1×B1=12×56:
-
合并结果:
根据公式 A×B=z2×102m+(z1−z2−z0)×10m+z0A \times B = z_2 \times 10^{2m} + (z_1 - z_2 - z_0) \times 10^m + z_0A×B=z2×102m+(z1−z2−z0)×10m+z0,这里 m=2m = 2m=2,代入计算:
直接验证 1234×5678=70066521234 \times 5678 = 70066521234×5678=7006652。
以下以两个十进制整数 A=12345678A = 12345678A=12345678,B=98765432B = 98765432B=98765432 为例,展示 Karatsuba 算法更进阶的计算过程(这里取 m=4m = 4m=4,即将整数拆分成 4 位一组):
五、实例进阶,递归Karatsuba算法
如果上次分解的结果值任然很大,可以对分解值继续使用Karatsuba算法来进行计算。
1. 拆分整数
- 对于整数 AAA:
A=1234×104+5678A = 1234 \times 10^4 + 5678A=1234×104+5678,所以 A1=1234A_1 = 1234A1=1234,A0=5678A_0 = 5678A0=5678。 - 对于整数 BBB:
B=9876×104+5432B = 9876 \times 10^4 + 5432B=9876×104+5432,所以 B1=9876B_1 = 9876B1=9876,B0=5432B_0 = 5432B0=5432。
2. 计算 3 个子乘积
-
计算 z2=A1×B1=1234×9876z_2 = A_1 \times B_1 = 1234 \times 9876z2=A1×B1=1234×9876:
由于这两个数还是较大的 4 位整数,我们继续对其使用 Karatsuba 算法进行拆分(取 m=2m = 2m=2,将 4 位再拆分成 2 位一组)来计算。- 对 A1=1234A_1 = 1234A1=1234 拆分:A11=12A_{11} = 12A11=12,A10=34A_{10} = 34A10=34(即 A1=12×102+34A_1 = 12 \times 10^2 + 34A1=12×102+34)。
- 对 B1=9876B_1 = 9876B1=9876 拆分:B11=98B_{11} = 98B11=98,B10=76B_{10} = 76B10=76(即 B1=98×102+76B_1 = 98 \times 10^2 + 76B1=98×102+76)。
- 计算 3 个子子乘积:
- 计算 z22=A11×B11=12×98z_{22} = A_{11} \times B_{11} = 12 \times 98z22=A11×B11=12×98:
通过传统乘法可得:
12×98=(10+2)×98=10×98+2×98=980+196=117612 \times 98 = (10 + 2) \times 98 = 10 \times 98 + 2 \times 98 = 980 + 196 = 117612×98=(10+2)×98=10×98+2×98=980+196=1176。 - 计算 z20=A10×B10=34×76z_{20} = A_{10} \times B_{10} = 34 \times 76z20=A10×B10=34×76:
同样使用传统乘法:
34×76=(30+4)×76=30×76+4×76=2280+304=258434 \times 76 = (30 + 4) \times 76 = 30 \times 76 + 4 \times 76 = 2280 + 304 = 258434×76=(30+4)×76=30×76+4×76=2280+304=2584。 - 计算 z21=(A11+A10)×(B11+B10)=(12+34)×(98+76)z_{21} = (A_{11} + A_{10}) \times (B_{11} + B_{10}) = (12 + 34) \times (98 + 76)z21=(A11+A10)×(B11+B10)=(12+34)×(98+76):
先算括号内的加法:12+34=4612 + 34 = 4612+34=46,98+76=17498 + 76 = 17498+76=174。
然后计算乘积 46×17446 \times 17446×174:
46×174=46×(100+70+4)=46×100+46×70+46×4=4600+3220+184=800446 \times 174 = 46 \times (100 + 70 + 4) = 46 \times 100 + 46 \times 70 + 46 \times 4 = 4600 + 3220 + 184 = 800446×174=46×(100+70+4)=46×100+46×70+46×4=4600+3220+184=8004。
- 计算 z22=A11×B11=12×98z_{22} = A_{11} \times B_{11} = 12 \times 98z22=A11×B11=12×98:
- 合并 z2z_2z2 的结果:
根据公式 z2=z22×102m+(z21−z22−z20)×10m+z20z_2 = z_{22} \times 10^{2m} + (z_{21} - z_{22} - z_{20}) \times 10^m + z_{20}z2=z22×102m+(z21−z22−z20)×10m+z20(这里 m=2m = 2m=2),代入计算:
先算中间项 (z21−z22−z20)(z_{21} - z_{22} - z_{20})(z21−z22−z20):
z21−z22−z20=8004−1176−2584=4244z_{21} - z_{22} - z_{20} = 8004 - 1176 - 2584 = 4244z21−z22−z20=8004−1176−2584=4244。
再分别计算各项乘积并相加:
z22×104=1176×104=11760000z_{22} \times 10^4 = 1176 \times 10^4 = 11760000z22×104=1176×104=11760000。
(z21−z22−z20)×102=4244×102=424400(z_{21} - z_{22} - z_{20}) \times 10^2 = 4244 \times 10^2 = 424400(z21−z22−z20)×102=4244×102=424400。
z20=2584z_{20} = 2584z20=2584。
最后求和得到 z2z_2z2:
z2=11760000+424400+2584=12186984z_2 = 11760000 + 424400 + 2584 = 12186984z2=11760000+424400+2584=12186984。
-
计算 z0=A0×B0=5678×5432z_0 = A_0 \times B_0 = 5678 \times 5432z0=A0×B0=5678×5432:
同样采用 Karatsuba 算法进行拆分(取 m=2m = 2m=2,将 4 位拆分成 2 位一组)来计算。- 对 A0=5678A_0 = 5678A0=5678 拆分:A01=56A_{01} = 56A01=56,A00=78A_{00} = 78A00=78(即 A0=56×102+78A_0 = 56 \times 10^2 + 78A0=56×102+78)。
- 对 B0=5432B_0 = 5432B0=5432 拆分:B01=54B_{01} = 54B01=54,B00=32B_{00} = 32B00=32(即 B0=54×102+32B_0 = 54 \times 10^2 + 32B0=54×102+32)。
- 计算 3 个子子乘积:
- 计算 z02=A01×B01=56×54z_{02} = A_{01} \times B_{01} = 56 \times 54z02=A01×B01=56×54:
通过传统乘法:
56×54=(50+6)×54=50×54+6×54=2700+324=302456 \times 54 = (50 + 6) \times 54 = 50 \times 54 + 6 \times 54 = 2700 + 324 = 302456×54=(50+6)×54=50×54+6×54=2700+324=3024。 - 计算 z00=A00×B00=78×32z_{00} = A_{00} \times B_{00} = 78 \times 32z00=A00×B00=78×32:
计算可得:
78×32=(70+8)×32=70×32+8×32=2240+256=249678 \times 32 = (70 + 8) \times 32 = 70 \times 32 + 8 \times 32 = 2240 + 256 = 249678×32=(70+8)×32=70×32+8×32=2240+256=2496。 - 计算 z01=(A01+A00)×(B01+B00)=(56+78)×(54+32)z_{01} = (A_{01} + A_{00}) \times (B_{01} + B_{00}) = (56 + 78) \times (54 + 32)z01=(A01+A00)×(B01+B00)=(56+78)×(54+32):
先算括号内的加法:56+78=13456 + 78 = 13456+78=134,54+32=8654 + 32 = 8654+32=86。
然后计算乘积 134×86134 \times 86134×86:
134×86=134×(80+6)=134×80+134×6=10720+804=11524134 \times 86 = 134 \times (80 + 6) = 134 \times 80 + 134 \times 6 = 10720 + 804 = 11524134×86=134×(80+6)=134×80+134×6=10720+804=11524。
- 计算 z02=A01×B01=56×54z_{02} = A_{01} \times B_{01} = 56 \times 54z02=A01×B01=56×54:
- 合并 z0z_0z0 的结果:
根据公式 z0=z02×102m+(z01−z02−z00)×10m+z00z_0 = z_{02} \times 10^{2m} + (z_{01} - z_{02} - z_{00}) \times 10^m + z_{00}z0=z02×102m+(z01−z02−z00)×10m+z00(这里 m=2m = 2m=2),代入计算:
先算中间项 (z01−z02−z00)(z_{01} - z_{02} - z_{00})(z01−z02−z00):
z01−z02−z00=11524−3024−2496=6004z_{01} - z_{02} - z_{00} = 11524 - 3024 - 2496 = 6004z01−z02−z00=11524−3024−2496=6004。
再分别计算各项乘积并相加:
z02×104=3024×104=30240000z_{02} \times 10^4 = 3024 \times 10^4 = 30240000z02×104=3024×104=30240000。
(z01−z02−z00)×102=6004×102=600400(z_{01} - z_{02} - z_{00}) \times 10^2 = 6004 \times 10^2 = 600400(z01−z02−z00)×102=6004×102=600400。
z00=2496z_{00} = 2496z00=2496。
最后求和得到 z0z_0z0:
z0=30240000+600400+2496=30842896z_0 = 30240000 + 600400 + 2496 = 30842896z0=30240000+600400+2496=30842896。
-
计算 z1=(A1+A0)×(B1+B0)=(1234+5678)×(9876+5432)z_1 = (A_1 + A_0) \times (B_1 + B_0) = (1234 + 5678) \times (9876 + 5432)z1=(A1+A0)×(B1+B0)=(1234+5678)×(9876+5432):
先算括号内的加法:
1234+5678=69121234 + 5678 = 69121234+5678=6912,9876+5432=153089876 + 5432 = 153089876+5432=15308。
然后计算乘积 6912×153086912 \times 153086912×15308,这里我们再次使用 Karatsuba 算法进行拆分(取 m=3m = 3m=3,将 5 位左右的数拆分成 3 位和 2 位一组)来计算。- 对 691269126912 拆分:6912=6×103+9126912 = 6 \times 10^3 + 9126912=6×103+912,即 69121=66912_{1} = 669121=6,69120=9126912_{0} = 91269120=912。
- 对 153081530815308 拆分:15308=15×103+30815308 = 15 \times 10^3 + 30815308=15×103+308,即 153081=1515308_{1} = 15153081=15,153080=30815308_{0} = 308153080=308。
- 计算 3 个子子乘积:
- 计算 z12=69121×153081=6×15=90z_{12} = 6912_{1} \times 15308_{1} = 6 \times 15 = 90z12=69121×153081=6×15=90。
- 计算 z10=69120×153080=912×308z_{10} = 6912_{0} \times 15308_{0} = 912 \times 308z10=69120×153080=912×308:
通过传统乘法(过程略)可得 912×308=280896912 \times 308 = 280896912×308=280896。 - 计算 z11=(69121+69120)×(153081+153080)=(6+912)×(15+308)z_{11} = (6912_{1} + 6912_{0}) \times (15308_{1} + 15308_{0}) = (6 + 912) \times (15 + 308)z11=(69121+69120)×(153081+153080)=(6+912)×(15+308):
先算括号内的加法:6+912=9186 + 912 = 9186+912=918,15+308=32315 + 308 = 32315+308=323。
然后计算乘积 918×323918 \times 323918×323(过程略)可得 918×323=296514918 \times 323 = 296514918×323=296514。
- 合并 z1z_1z1 的结果:
根据公式 z1=z12×102m+(z11−z12−z10)×10m+z10z_1 = z_{12} \times 10^{2m} + (z_{11} - z_{12} - z_{10}) \times 10^m + z_{10}z1=z12×102m+(z11−z12−z10)×10m+z10(这里 m=3m = 3m=3),代入计算:
先算中间项 (z11−z12−z10)(z_{11} - z_{12} - z_{10})(z11−z12−z10):
z11−z12−z10=296514−90−280896=15528z_{11} - z_{12} - z_{10} = 296514 - 90 - 280896 = 15528z11−z12−z10=296514−90−280896=15528。
再分别计算各项乘积并相加:
z12×106=90×106=90000000z_{12} \times 10^6 = 90 \times 10^6 = 90000000z12×106=90×106=90000000。
(z11−z12−z10)×103=15528×103=15528000(z_{11} - z_{12} - z_{10}) \times 10^3 = 15528 \times 10^3 = 15528000(z11−z12−z10)×103=15528×103=15528000。
z10=280896z_{10} = 280896z10=280896。
最后求和得到 z1z_1z1:
z1=90000000+15528000+280896=105808896z_1 = 90000000 + 15528000 + 280896 = 105808896z1=90000000+15528000+280896=105808896。
3. 合并结果
根据公式 A×B=z2×102m+(z1−z2−z0)×10m+z0A \times B = z_2 \times 10^{2m} + (z_1 - z_2 - z_0) \times 10^m + z_0A×B=z2×102m+(z1−z2−z0)×10m+z0,这里 m=4m = 4m=4,代入计算:
- 先计算 z2×108z_2 \times 10^{8}z2×108:
z2×108=12186984×108=1218698400000000z_2 \times 10^{8} = 12186984 \times 10^{8} = 1218698400000000z2×108=12186984×108=1218698400000000。 - 再计算中间项 (z1−z2−z0)(z_1 - z_2 - z_0)(z1−z2−z0):
z1−z2−z0=105808896−12186984−30842896=62779016z_1 - z_2 - z_0 = 105808896 - 12186984 - 30842896 = 62779016z1−z2−z0=105808896−12186984−30842896=62779016。 - 然后计算中间项乘以 10410^4104:
(z1−z2−z0)×104=62779016×104=627790160000(z_1 - z_2 - z_0) \times 10^4 = 62779016 \times 10^4 = 627790160000(z1−z2−z0)×104=62779016×104=627790160000。 - 最后加上 z0z_0z0:
z0=30842896z_0 = 30842896z0=30842896。
将上述各项相加可得:122497633244289612249763324428961224976332442896
六、时间复杂度分析
传统的大整数乘法时间复杂度为 O(n2)O(n^2)O(n2),而Karatsuba算法通过分治策略和上述的数学变换,每次将大整数分成两半进行处理,其时间复杂度可以表示为递归式 T(n)=3T(n/2)+O(n)T(n) = 3T(n/2) + O(n)T(n)=3T(n/2)+O(n)(其中 O(n)O(n)O(n) 主要来自于加法、减法以及最后的合并操作等开销)。
根据主定理(Master Theorem)等复杂度分析方法,可以得出Karatsuba算法的时间复杂度为 O(nlog23)≈O(n1.585)O(n^{\log_2 3}) \approx O(n^{1.585})O(nlog23)≈O(n1.585)。这意味着随着整数位数 nnn 的不断增大,Karatsuba算法相比传统乘法在计算效率上的优势会越来越明显,尤其是处理几百位甚至上千位以上的大整数时,能极大地减少计算时间和资源消耗。
七、应用场景
-
编程语言中的大整数运算库:
在很多编程语言中,如Java的BigInteger
类、Python中处理大整数的相关模块等,当涉及到超出基本数据类型所能表示范围的大整数乘法运算时,内部会自动采用类似Karatsuba算法(有些可能还会结合更先进的分治或优化算法)来提高计算效率,确保能够准确且高效地处理大整数的各种数学运算。 -
密码学领域:
像RSA加密算法等,在生成密钥对、进行加密和解密操作过程中,经常需要处理非常大的整数(例如几百位甚至上千位的质数等),Karatsuba算法及其衍生的高效大整数乘法算法能够加快这些大整数运算的速度,从而提升整个加密系统的性能,保证密码学操作在合理的时间内完成。 -
高精度科学计算:
在一些科学计算场景中,例如天文学中对星系距离等超大数值的精确计算、物理学中微观粒子相关的高精度数值模拟等,需要对大整数进行精确的乘法运算,Karatsuba算法可以帮助科研人员更高效地完成这些计算任务,减少计算时间成本,提高科研效率。
总之,Karatsuba算法凭借其巧妙的分治思想和对乘法次数的优化,在众多需要处理大整数乘法的领域发挥着重要作用,并且也是后续很多更先进大整数运算优化算法的基础。这条消息已经在编辑器中准备就绪。你想如何调整这篇文档?请随时告诉我。