分治法的经典问题——大整数相乘

本文介绍了一种利用分治法提高大整数乘法效率的算法。通过对传统乘法方法的时间复杂度分析,引入分治法将问题分解为更小规模的子问题,并通过递归调用解决这些子问题,最终合并结果得到答案。此方法显著降低了时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分治法的原理

讨论问题时,先来了解一下什么是分治法。

分治法的意思就是,分而治之,也就是把一个问题,拆分成几个小问题,最后再汇总解决的方法

这里写图片描述

通过大整数相乘问题来了解分治法

假如现在我们要求两个大整数相乘的乘积,如1234 * 1234(这里为了了分析简便,所以不举形如1234567891234567这样的大整数,不必要在此纠结),那么按照我们小学学的乘法,就是用乘数的每一项去和1234相乘,这样很明显,算法的时间复杂度是O(n^2),效率很低下,那么有没有一种更好的方式?我们可以使用分治法来解决。

这里写图片描述

算法分析

  1. 首先将X和Y分成A,B,C,D
  2. 此时将X和Y的乘积转化为图中的式子,把问题转化为求解式子的值

看起来好像有点变化,分析一下:对这个式子,我们一共要进行4次 n / 2的乘法(AC2次, AD, BC)和 3次加法,因而该算法的时间复杂度为:

T(n) = 4 * T(n / 2) + θ(n)
通过master定理可以求得 T(n) = θ(n ^ 2),跟小学算法的时间复杂度没有区别。

但是我们再来看看,我们是否可以用加法来换取乘法?因为多一个加法操作,也是常数项,对时间复杂度没有影响,如果减少一个乘法则不同。

XY=AC2^n+[(A-B)(D-C)+AC+BD]2^n/2+BD

现在的时间复杂度为:

T(n) = 3 * T(n / 2) + θ(n),通过master定理求得,T(n) = O(n^log2(3) ) = O(n^1.59 )。

Master定理:

这里写图片描述

Master定理实例:
这里写图片描述

时间复杂度O的意义

这里写图片描述

实例分析

对X = 1234 和 Y = 5678来分析:

divideConquer(1234, 5678, 4)
————————————————————————
X=1234 | A=12 | B=34 | A-B=-22

Y=5678 | C=56 | D=78 | D-C=22

三次递归:
AC = divideConquer(12, 56, 2)
BD = divideConquer(34, 78, 2)
(A-B)(D-C) = divideConquer(-22, 22, 2)

AC递归
————————————————————————

X=12 | A1=1 | B1=2 | A1-B1=-1

Y=56 | C1=5 | D1=6 | D1-C1=1

A1*C1 = divideConquer(1, 5, 1) = 5
B1*D1 = divideConquer(2, 6, 1) = 12
(A1-B1) * (D1-C1) = divideConquer(-1, 1, 1) = -1;

所以AC递归求得的值为:5 * 10 ^ 2 + (-1 + 5 + 12)* 10 + 12 = 672

同理可得 BD = 2652 和 (A-B)(D-C) = -484

最终可得 X * Y = 7006652

代码实现

#include<cstdio>
#include<cmath>

using namespace std;

#define SIGN(A) ((A > 0) ? 1 : -1) 
int divideConquer(int X, int Y, int n){
    int sign = SIGN(X) * SIGN(Y);
    int x = abs(X);
    int y = abs(Y);
    if(x == 0 || y == 0){
        return 0;
    }else if(n == 1){
        return sign * x * y;
    }else{
        int A = (int) x / pow(10, (int)(n / 2));
        int B = x - A * pow(10, n / 2);
        int C = (int) y / pow(10, (int)(n / 2));
        int D = y - C * pow(10, n / 2);
        int AC = divideConquer(A, C, n / 2);
        int BD = divideConquer(B, D, n / 2);
        int ABDC = divideConquer((A - B), (D - C), n / 2) + AC + BD;
        return sign * (AC * pow(10 , n) + ABDC * pow(10, (int)(n / 2)) + BD); 
    }
}

int main(){
    int x, y, n;
    scanf("%d%d%d", &x, &y, &n);
    printf("x 和 y的乘积为:%d", divideConquer(x, y, n));
}

结果:

这里写图片描述

### 关于大整数相乘的PTA解题思路 在处理大整数相乘问题时,由于标准数据类型的限制(如C++中的`int`或Python中的`int`),当数值过大时可能会超出范围。因此,在解决此类问题时通常采用字符串或其他方法来存储和操作这些超大数据。 #### 方法一:模拟手工乘法 通过将数字转换成字符数组的形式逐位进行乘法运算并累加结果,这种方法类似于我们手算两位多位数相乘的过程[^4]。具体实现如下: ```python def multiply(num1: str, num2: str) -> str: if num1 == "0" or num2 == "0": return "0" result = [0] * (len(num1) + len(num2)) # 反向遍历两串数字 pos = len(result) - 1 for n1 in reversed(num1): tempPos = pos for n2 in reversed(num2): result[tempPos] += int(n1) * int(n2) result[tempPos - 1] += result[tempPos] // 10 result[tempPos] %= 10 tempPos -= 1 pos -= 1 start = 0 while start < len(result) and result[start] == 0: start += 1 return ''.join(map(str, result[start:])) # 测试函数 print(multiply("123", "456")) # 输出:"56088" ``` 此代码片段展示了如何利用列表作为中间容器保存每一位的结果,并最终拼接形成完整的乘积字符串。 #### 方法二:分治策略 对于非常大的整数,可以考虑使用更高效的算法——分治法。其核心思想是把原始的大整数拆分成较小的部分分别计算后再组合起来得到最后答案。例如Karatsuba算法就是基于这种理念设计出来的快速多项式乘法技术之一。 假设我们需要求解 X × Y ,其中 X 和 Y 都是非常大的正整数。我们可以将其写成下面这样的形式: 设 \( A \),\( B \),\( C \),\( D \) 均为长度约为 N/2 的子项, \[ XY=(AX+B)(CX+D)=ACX^{2}+(AD+BC)X+BD\] 进一步简化可得 Karatsuba 公式: \[XY=AC\times{10^n}+[[(A−B)\times(D−C)+AC+BD]\times{10^\frac{n}{2}}]+BD\] 上述表达式的优点在于减少了传统方式所需的三次独立乘法至两次主要乘法以及一次辅助加减操作即可完成整个过程。 ### 注意事项 - 当输入的数据量特别庞大时,建议选用高级语言自带库或者第三方支持包来进行高效管理。 - 对于某些特殊场景下的边界条件需格外留意,比如零值情况、负号处理等。
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值