大数相乘

编写两个任意位数得大数相乘的程序,给出计算结果。

题目描述: 输出两个不超过100位的大整数的乘积。
输入: 输入两个大整数,如1234567 和 123
输出: 输出乘积,如:151851741

分析

  • 以上述为例,倘若手算的话,即把123的每一位都乘以1234567然后相加。下面我们来简单实现这种思路。
def test(x,y):
    if x == 0 or y == 0:
        return 0
    import math
    leny = int(math.log10(y))+1
    res = 0
    for i in range(leny-1,-1,-1):
        res += int(str(y)[i])*x*10**(leny-1-i)
    return res

x = 1234567
y = 123
print(test(x,y))
## 输出 151851741
  • 改进上述方法

先不算任何的进位,也就是说,将每一位相乘,相加的结果保存到同一个位置,到最后才计算进位

例如:计算98×21,步骤如下

          9  8
×       2  1
-------------
       (9)(8)  <---- 第1趟: 98×1的每一位结果 
  (18)(16)     <---- 第2趟: 98×2的每一位结果 
-------------
  (18)(25)(8)  <---- 这里就是相对位的和,还没有累加进位
def test(x,y):
    x = change(x,[])
    y = change(y,[])
    tmp = [0 for _ in range(len(x)+len(y))]
    for i in range(len(x)):
        for j in range(len(y)):
            tmp[i+j] += x[i]*y[j]

    carry = 0
    for i in range(len(tmp)):
        hh  =  (tmp[i]+carry)
        tmp[i] = hh%10
        carry = hh //10

    res = 0
    for i in range(len(tmp)):
        res += tmp[i]*10**(i)
    return res

def change(x,res):
    # if x == 0:
    #     return 
    # res.append(x%10)
    # change(x//10,res)
    # return res
     while x != 0:
         res.append(x%10)
         x = x//10
     return res

x = 1234567
y = 123
print(test(x,y))
  • Karatsuba算法

Karatsuba算法主要应用于两个大数的相乘,原理是将大数分成两段后变成较小的数位,然后做3次乘法,并附带少量的加法操作和移位操作。

现有两个大数,x,y。

首先将x,y分别拆开成为两部分,可得x1,x0,y1,y0。他们的关系如下:

x = x1 * 10^m + x0;
y = y1 * 10^m + y0。其中m为正整数,m < n,且x0,y0 小于 10^m。

那么 xy = (x1 * 10^m + x0)*(y1 * 10^m + y0) = z2 * 10^(2m) + z1 * 10^m + z0,其中:

z2 = x1 * y1;
z1 = x1 * y0 + x0 * y1;
z0 = x0 * y0。

此步骤共需4次乘法,但是由Karatsuba改进以后仅需要3次乘法。因为:

z1 = x1 * y0+ x0 * y1
z1 = (x1 + x0) * (y1 + y0) - x1 * y1 - x0 * y0,

故z1 便可以由一次乘法及加减法得到。

实例展示

设x = 12345,y=6789,令m=3。那么有:

12345 = 12 * 1000 + 345;
6789 = 6 * 1000 + 789。

下面计算:

z2 = 12 * 6 = 72;
z0 = 345 * 789 = 272205;
z1 = (12 + 345) * (6 + 789) - z2 - z0 = 11538。

然后我们按照移位公式(xy = z2 10^(2m) + z1 10^(m) + z0)可得:

xy = 72 * 10002 + 11538 * 1000 + 272205 = 83810205。
#### Python 递归有最大深度限制
def karatsuba(x,y):
    import math
    lenx = int(math.log10(x))
    leny = int(math.log10(y))
    m = min(lenx,leny)
    if m==1:
        return x*y

    x1,x0 = x//(10**m),x%(10**m)
    y1,y0 = y//(10**m),y%(10**m)

    z2 = karatsuba(x1,y1)
    z0 = karatsuba(x0,y0)
    z1 = karatsuba((x1+x0),(y1+y0))-z2-z0

    return z2*10**(2*m)+z1*10**m+z0

print(karatsuba(99,9999))