python 使用分而治之算法进行快速乘法的 Karatsuba 算法(唐叶算法)

        给定两个表示两个整数值的二进制字符串,求两个字符串的乘积。例如,如果第一个位串是“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 类的“乘法”方法是针对三个乘积中的每一个递归调用的。  

  • 29
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值