给定两个表示两个整数值的二进制字符串,求两个字符串的乘积。例如,如果第一个位串是“1100”,第二个位串是“1010”,则输出应为 120。
为了简单起见,让两个字符串的长度相同并为n。
一种天真的方法是遵循我们在学校学习的过程。逐一取出第二个数字的所有位并将其与第一个数字的所有位相乘。最后将所有乘法相加。该算法需要 O(n^2) 时间。
使用Divide and Conquer,我们可以以较低的时间复杂度将两个整数相乘。我们将给定的数字分成两半。设给定数字为 X 和 Y。
为简单起见,我们假设 n 是偶数
X = Xl*2 n/2 + Xr [Xl 和 Xr 包含 X 的最左边和最右边的 n/2 位]
Y = Yl*2 n/2 + Yr [Yl 和 Yr 包含 Y 的最左边和最右边的 n/2 位]
乘积 XY 可以写如下。
XY = (Xl*2 n/2 + Xr)(Yl*2 n/2 + Yr)
= 2 n XlYl + 2 n/2 (XlYr + XrYl) + XrYr
如果我们看一下上面的公式,有四次大小为n/2的乘法,所以我们基本上将大小为n的问题分为四个大小为n/2的子问题。但这并没有帮助,因为递归 T(n) = 4T(n/2) + O(n) 的解是 O(n^2)。该算法的棘手部分是将中间两项更改为其他形式,这样只需一次额外的乘法就足够了。以下是中间两项的棘手表达。
XlYr + XrYl = (Xl + Xr)(Yl + Yr) - XlYl- XrYr
所以 XY 的最终值变为
XY = 2 n XlYl + 2 n/2 * [(Xl + Xr)(Yl + Yr) - XlYl - XrYr] + XrYr
通过上述技巧,递推式变为 T(n) = 3T(n/2) + O(n) 并且该递推式的解为 O(n 1.59 )。
如果输入字符串的长度不同且不均匀怎么办?为了处理不同长度的情况,我们在开头附加 0。为了处理奇数长度,我们将floor(n/2)位放在左半部分,将ceil(n/2)位放在右半部分。因此 XY 的表达式变为以下形式。
XY = 2 2ceil(n/2) XlYl + 2 ceil(n/2) * [(Xl + Xr)(Yl + Yr) - XlYl - XrYr] + XrYr
上述算法称为Karatsuba算法,它可以用于任何基础。
以下是上述算法的 python 实现:
# Python implementation of Karatsuba algorithm for bit string multiplication.
# Helper method: given two unequal sized bit strings, converts them to
# same length by adding leading 0s in the smaller string. Returns the
# the new length
def make_equal_length(str1, str2):
len1 = len(str1)
len2 = len(str2)
if len1 < len2:
for i in range(len2 - len1):
str1 = '0' + str1
return len2
elif len1 > len2:
for i in range(len1 - len2):
str2 = '0' + str2
return len1 # If len1 >= len2
# The main function that adds two bit sequences and returns the addition
def add_bit_strings(first, second):
result = "" # To store the sum bits
# make the lengths same before adding
length = make_equal_length(first, second)
carry = 0 # Initialize carry
# Add all bits one by one
for i in range(length-1, -1, -1):
first_bit = int(first[i])
second_bit = int(second[i])
# boolean expression for sum of 3 bits
sum = (first_bit ^ second_bit ^ carry) + ord('0')
result = chr(sum) + result
# boolean expression for 3-bit addition
carry = (first_bit & second_bit) | (second_bit & carry) | (first_bit & carry)
# if overflow, then add a leading 1
if carry:
result = '1' + result
return result
# A utility function to multiply single bits of strings a and b
def multiply_single_bit(a, b):
return int(a[0]) * int(b[0])
# The main function that multiplies two bit strings X and Y and returns
# result as long integer
def multiply(X, Y):
# Find the maximum of lengths of x and Y and make length
# of smaller string same as that of larger string
n = max(len(X), len(Y))
X = X.zfill(n)
Y = Y.zfill(n)
# Base cases
if n == 0: return 0
if n == 1: return int(X[0])*int(Y[0])
fh = n//2 # First half of string
sh = n - fh # Second half of string
# Find the first half and second half of first string.
Xl = X[:fh]
Xr = X[fh:]
# Find the first half and second half of second string
Yl = Y[:fh]
Yr = Y[fh:]
# Recursively calculate the three products of inputs of size n/2
P1 = multiply(Xl, Yl)
P2 = multiply(Xr, Yr)
P3 = multiply(str(int(Xl, 2) + int(Xr, 2)), str(int(Yl, 2) + int(Yr, 2)))
# Combine the three products to get the final result.
return P1*(1<<(2*sh)) + (P3 - P1 - P2)*(1<<sh) + P2
if __name__ == '__main__':
print(multiply("1100", "1010"))
print(multiply("110", "1010"))
print(multiply("11", "1010"))
print(multiply("1", "1010"))
print(multiply("0", "1010"))
print(multiply("111", "111"))
print(multiply("11", "11"))
输出
120
60
30
10
0
49
9
时间复杂度:上述解决方案的时间复杂度为 O(n log 2 3 ) = O(n 1.59 )。
使用另一种分而治之算法,即快速傅里叶变换,可以进一步提高乘法的时间复杂度。我们很快将作为单独的帖子讨论快速傅立叶变换。
辅助空间: O(n)
练习:
上面的程序返回一个 long int 值,不适用于大字符串。扩展上面的程序以返回字符串而不是 long int 值。
解决方案:
大数的乘法过程是计算机科学中的一个重要问题。给定方法使用分而治之的方法。
运行代码来查看普通二元乘法和 Karatsuba 算法的时间复杂度比较。 您可以在此存储库
中查看完整代码
例子:
第一个二进制输入:101001010101010010101001010100101010010101010010101第二个二进制输入:101001010101010010101001010100101010010101010010101十进制输出:无可呈现的输出:2.1148846e+30
第一个二进制输入:1011第二个二进制输入:1000十进制输出:88输出:5e-05
# Importing required module
import math
def addBinary(a, b):
a_bigint = int(a, 2)
b_bigint = int(b, 2)
result = bin(a_bigint + b_bigint)[2:]
return result
def shiftLeft(string, n):
return string + '0' * n
def classicalMultiply(str1, str2):
result = '0'
n = len(str2)
for i in range(n):
if str2[n - 1 - i] == '1':
result = addBinary(result, shiftLeft(str1, i))
return result
def karatsubaMultiply(X, Y):
n = max(len(X), len(Y))
# Make the lengths equal
X = X.rjust(n, '0')
Y = Y.rjust(n, '0')
if n == 1:
return bin(int(X, 2) * int(Y, 2))[2:]
m = n // 2
Xl = X[:m]
Xr = X[m:]
Yl = Y[:m]
Yr = Y[m:]
P1 = karatsubaMultiply(Xl, Yl)
P2 = karatsubaMultiply(Xr, Yr)
P3 = karatsubaMultiply(addBinary(Xl, Xr), addBinary(Yl, Yr))
C1 = shiftLeft(P1, 2 * (n - m))
C2 = shiftLeft(addBinary(subtractBinary(P3, addBinary(P1, P2)), P2), n - m)
return addBinary(addBinary(C1, C2), P2)
def subtractBinary(a, b):
a_bigint = int(a, 2)
b_bigint = int(b, 2)
result = bin(a_bigint - b_bigint & (2 ** (max(len(a), len(b))) - 1))[2:]
return result
if __name__ == "__main__":
# Given binary numbers
first_number = "011011010100"
second_number = "10111010111"
# Classical Algorithm
print("Classical Algorithm:")
classic_result = classicalMultiply(first_number, second_number)
print("Binary Result:", classic_result)
print("Decimal Result:", int(classic_result, 2))
# Karatsuba Algorithm
print("\nKaratsuba Algorithm:")
karatsuba_result = karatsubaMultiply(first_number, second_number)
print("Binary Result:", karatsuba_result)
print("Decimal Result:", int(karatsuba_result, 2))
时间复杂度:
二进制字符串乘法的经典方法和Karatsuba方法的时间复杂度都是O(n^2)。
在经典方法中,时间复杂度为O(n^2),因为循环迭代了 n 次。 addBinary() 方法的时间复杂度是恒定的,因为循环最多运行两次迭代。
在 Karatsuba 方法中,时间复杂度为O(n^2),因为对三个产品中的每一个都会递归调用 Karasuba 类的“乘法”方法。 addStrings() 方法的时间复杂度是恒定的,因为循环最多运行两次迭代。
辅助空间:
二进制字符串乘法的经典方法和Karatsuba方法的辅助空间都是O(n)。
在经典方法中,辅助空间是O(n),因为循环迭代了 n 次并且使用单个字符串来存储结果。addBinary() 方法的空间复杂度是恒定的,因为循环最多运行两次迭代。
在Karatsuba 方法中,辅助空间为O(n),因为Karatsuba 类的“乘法”方法是针对三个乘积中的每一个递归调用的。