题目
给定两个以字符串形式表示的非负整数num1和num2,返回num1和num2的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的BigInteger库或直接将输入转换为整数。
备注:
(1)1 <= num1.length, num2.length <= 200。
(2)num1和num2只能由数字组成。
(3)num1和num2都不包含任何前导零,除了数字0本身。
示例 1:
输入: num1 = "2", num2 = "3"
输出: "6"
示例 2:
输入: num1 = "123", num2 = "456"
输出: "56088"
手动实现乘法
使用手动实现乘法来解决本题是一种直观且有效的方法,它模拟了我们在纸上进行大数乘法的方式。求解本题的主要步骤如下。
1、初始化。创建一个结果数组result用于存储乘积的每一位。
2、遍历乘法。从右到左遍历num1的每一位,对于num1的每一位,从右到左遍历num2的每一位。
3、计算乘积。计算每一位的乘积,并加上进位。
4、更新进位。更新进位,并将结果存入result数组。
5、处理进位。遍历完成后,处理最后一次进位,并将result数组转换为字符串。
根据上面的算法步骤,我们可以得出下面的示例代码。
def string_multiply_manually(num1, num2):
result = [0] * (len(num1) + len(num2))
# 遍历num1的每一位
for i in range(len(num1) - 1, -1, -1):
n1 = int(num1[i])
# 遍历num2的每一位
for j in range(len(num2) - 1, -1, -1):
n2 = int(num2[j])
# 计算乘积并加上进位
product = n1 * n2 + result[i + j + 1]
# 更新进位
carry = product // 10
result[i + j + 1] = product % 10
# 更新当前位置的值
result[i + j] += carry
# 处理前导零
i = 0
while i < len(result) and result[i] == 0:
i += 1
# 转换成字符串
return ''.join(map(str, result[i:]))
print(string_multiply_manually("2", "3"))
print(string_multiply_manually("123", "456"))
Karatsuba算法
Karatsuba算法是由著名的俄罗斯数学家Karatsuba发明的,它以其在快速乘法算法领域的工作而闻名。Karatsuba算法利用了分治的思想,通过将大数分解为较小的部分来减少乘法的次数。其算法的主要特点如下:
1、将两个大数分别拆分为高位和低位两个部分。
2、利用公式 xy = (a * 10^n + b) * (c * 10^n + d) = ac * 10^(2n) + ((ad + bc) * 10^n) + bd 来计算乘法结果。其中a、b、c、d分别表示大数的高位和低位部分,n表示大数的位数一半的位置。
3、通过递归地应用该算法,可以有效地减少所需的乘法次数。
使用Karatsuba算法求解本题的主要步骤如下。
1、基础情况。如果任意一个数字的长度小于等于1,则直接返回它们的乘积。
2、分割数字。将两个数字分割成高位和低位,为后面的步骤做准备。
3、递归计算。递归地计算高位乘积、低位乘积和交叉乘积。
4、组合结果。利用上述结果,计算出最终的乘积。
根据上面的算法步骤,我们可以得出下面的示例代码。
def string_multiply_by_karatsuba(num1, num2):
def str_to_int(s):
return int(s)
def int_to_str(n):
return str(n)
# 基本情况
if num1 == "0" or num2 == "0":
return "0"
if len(num1) < 10 or len(num2) < 10:
return int_to_str(str_to_int(num1) * str_to_int(num2))
# 分割数字
n = max(len(num1), len(num2))
half = n // 2
high1 = num1[:half]
low1 = num1[half:]
high2 = num2[:half]
low2 = num2[half:]
# 递归计算
z0 = string_multiply_by_karatsuba(low1, low2)
z1 = string_multiply_by_karatsuba((str_to_int(low1) + str_to_int(high1)),
(str_to_int(low2) + str_to_int(high2)))
z2 = string_multiply_by_karatsuba(high1, high2)
# 计算最终结果
result = int_to_str(str_to_int(z2) * (10 ** (2 * half)) +
((str_to_int(z1) - str_to_int(z2) - str_to_int(z0)) *
(10 ** half)) + str_to_int(z0))
return result
print(string_multiply_by_karatsuba("2", "3"))
print(string_multiply_by_karatsuba("123", "456"))
总结
手动实现乘法时,对于两个长度为n的数字,需要遍历每一个数字位,因此时间复杂度为O(n^2)。空间复杂度为O(n),因为需要一个长度为2n的数组来保存乘积的结果。手动实现乘法的概念比较直观,容易理解和实现。但对于非常大的数字,时间复杂度较高,效率较低。
Karatsuba算法通过递归地将大数分解为较小的部分来减少乘法次数,其时间复杂度大约为O(n^log2(3)) = O(n^1.585),这比传统的O(n^2)更高效。对于较大的数字,Karatsuba算法比传统的乘法更快。但其实现更为复杂,需要理解递归和分治的概念。
实际上,快速傅立叶变换 (Fast Fourier Transform, 即FFT) 也可以用来高效地计算大数乘法。但其实现复杂性高,需要较强的数学背景支持,这里就不再赘述了。
💡 需要《Python面试宝典》完整源码的大佬们,可订阅专栏后,搜索微信公众号“希望睿智”私信获取。