暴力相加
下面展示一些 内联代码片
。
def simple_multi(stra, strb):
aa = list(stra)
bb = list(strb)
lena = len(stra)
lenb = len(strb)
result = [0 for i in range(lena+lenb)]
for i in range(lena):
for j in range(lenb):
result[lena-i-1+lenb-j-1] += int(aa[i])*int(bb[j])
for i in range(len(result)-1):
if result[i] >= 10:
result[i+1] += result[i]//10
result[i] = result[i] % 10
return list2str(result[::-1])
分治法
下面展示一些 内联代码片
。
# 首先将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 * 10m + x0)(y1 * 10
# m + y0)
# =z2 * 102
# m + z1 * 10
# m + z0,其中:
# 此步骤共需4次乘法,
def fenzhi_multi(x1, x2):
l1 = len(x1)
l2 = len(x2)
if x1 == '0' or x2 == '0' or x1 == '' or x2 == '':
return '0'
elif l1 == 1 or l2 == 1:
val = str(int(x1) * int(x2))
return val
else:
cut_len = int(min(l1, l2) / 2)
cut_point1 = l1 - cut_len
cut_point2 = l2 - cut_len
x1_0 = x1[:cut_point1]
x1_1 = x1[cut_point1:]
x2_0 = x2[:cut_point2]
x2_1 = x2[cut_point2:]
ac = fenzhi_multi(x1_0, x2_0)
bd = fenzhi_multi(x1_1, x2_1)
ad = fenzhi_multi(x1_0, x2_1)
bc = fenzhi_multi(x1_1, x2_0)
ac_10n = ac + '0' * 2 * cut_len
adbc = add(ad, bc) + '0' * cut_len
res = add(add(ac_10n, adbc), bd)
return res
改进的分治法
# 但是由Karatsuba改进以后仅需要3次乘法。因为:
# z1 = x1 * y0 + x0 * y1
# z1 = (x1 + x0) * (y1 + y0) - x1 * y1 - x0 * y0,
def changed_fenzhi_multi(x1, x2):
if x1 == '0' or x2 == '0' or x1 == '' or x2 == '':
return '0'
sign = 1
if x1[0] == '-':
sign *= -1
x1 = x1[1:]
if x2[0] == '-':
sign *= -1
x2 = x2[1:]
l1 = len(x1)
l2 = len(x2)
if l1 == 1 or l2 == 1:
val = str(int(x1) * int(x2))
if sign == -1:
val = val[::-1] + '-'
val = val[::-1]
return val
else:
cut_len = int(min(l1, l2)/2)
cut_point1 = l1 - cut_len
cut_point2 = l2 - cut_len
x1_0 = x1[:cut_point1]
x1_1 = x1[cut_point1:]
x2_0 = x2[:cut_point2]
x2_1 = x2[cut_point2:]
ac = changed_fenzhi_multi(x1_0, x2_0)
bd = changed_fenzhi_multi(x1_1, x2_1)
a_add_b = add(x1_0, x1_1)
d_add_c = add(x2_1, x2_0)
abcd = changed_fenzhi_multi(a_add_b, d_add_c)
ac_10n = ac + '0'*2*cut_len
mid = sub1(sub1(abcd, ac), bd)
mid = mid + '0' * cut_len
val = add(add(ac_10n, mid), bd)
if sign == -1:
val = val[::-1] + '-'
val = val[::-1]
return val
时间 | 普通大整数乘法 | 分治大整数乘法 | 改进的分治大整数乘法 |
---|---|---|---|
10位 | 0.000044 | 0.0003371238708496094 | 0.000533342361450 |
100位 | 0.002929210662841 | 0.03171944618225098 | 0.04110884666442871 |
1000位 | 0.3304471969604492 | 3.543729782104492 | 1.418264627456665 |
10000位 | 33.64151573181152 | 300.8758816719055 | 58.203367471694946 |
100000位 | 5678.60 | 30478.068567276 | 56603.10471582413 |
56603.10471582413 |
全部源码
import time
import random
def add(n1, n2):
if n1[0] == '-' and n2[0] != '-':
return sub1(n2, n1[1:])
elif n1[0] != '-' and n2[0] == '-':
return sub1(n1, n2[1:])
elif n1[0] == '-' and n2[0] == '-':
temp = add(n1[1:], n2[1:])
temp = temp[::-1] + '-'
temp = temp[::-1]
return temp
n1 = n1[::-1]
n2 = n2[::-1]
# 补齐到和的最大位数
if len(n1)<len(n2):
n2 += '0'
n1 += '0'*(len(n2)-len(n1))
else:
n1 += '0'
n2 += '0'*(len(n1)-len(n2))
carry,sum = 0,''
for i in range(len(n1)):
cur_sum = int(n1[i]) + int(n2[i]) + carry
carry = cur_sum // 10
sum += str(cur_sum % 10)
sum = sum[::-1]
# 去掉前面的零
while len(sum)>1 and sum[0]=='0':
sum = sum[1:]
return sum
def sub1(n1, n2):
if n1 == '' and n2 == '':
return '0'
elif n1 == '':
return sub1('0', n2)
elif n2 == '':
return n1
if n1[0] != '-' and n2[0] == '-':
return add(n1, n2[1:])
elif n1[0] == '-' and n2[0] == '-':
return sub1(n2[1:], n1[1:])
elif n1[0] == '-' and n2[0] != '-':
temp = add(n1[1:], n2)
temp = temp[::-1] + '-'
temp = temp[::-1]
return temp
n1 = n1[::-1]
n2 = n2[::-1]
if len(n1) < len(n2):
n1 += '0' * (len(n2) -len(n1))
else:
n2 += '0' * (len(n1) -len(n2))
carry, sub = 0, ''
for i in range(len(n1)):
cur_sub = int(n1[i]) - carry - int(n2[i])
carry = 0
if cur_sub < 0:
carry = 1
cur_sub += 10
sub += str(cur_sub)
if carry == 1:
carry, sub = 0, ''
for i in range(len(n1)):
cur_sub = int(n2[i]) - carry - int(n1[i])
carry = 0
if cur_sub < 0:
carry = 1
cur_sub += 10
sub += str(cur_sub)
sub += '-'
sub = sub[::-1]
while len(sub) > 1 and sub[1] == '0':
sub = sub[0] + sub[2:]
else:
sub = sub[::-1]
while len(sub)>1 and sub[0] == '0':
sub = sub[1:]
return sub
def list2str(li):
while li[0] == 0:
del li[0]
res = ''
for i in li:
res += str(i)
return res
def simple_multi(stra, strb):
aa = list(stra)
bb = list(strb)
lena = len(stra)
lenb = len(strb)
result = [0 for i in range(lena+lenb)]
for i in range(lena):
for j in range(lenb):
result[lena-i-1+lenb-j-1] += int(aa[i])*int(bb[j])
for i in range(len(result)-1):
if result[i] >= 10:
result[i+1] += result[i]//10
result[i] = result[i] % 10
return list2str(result[::-1])
def number_generate(n):
num1 = ''
num2 = ''
for i in range(n):
num1 += str(random.randint(0, 9))
num2 += str(random.randint(0, 9))
return num1, num2
# 首先将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 * 10m + x0)(y1 * 10
# m + y0)
# =z2 * 102
# m + z1 * 10
# m + z0,其中:
# 此步骤共需4次乘法,但是由Karatsuba改进以后仅需要3次乘法。因为:
# z1 = x1 * y0 + x0 * y1
# z1 = (x1 + x0) * (y1 + y0) - x1 * y1 - x0 * y0,
def fenzhi_multi(x1, x2):
l1 = len(x1)
l2 = len(x2)
if x1 == '0' or x2 == '0' or x1 == '' or x2 == '':
return '0'
elif l1 == 1 or l2 == 1:
val = str(int(x1) * int(x2))
return val
else:
cut_len = int(min(l1, l2) / 2)
cut_point1 = l1 - cut_len
cut_point2 = l2 - cut_len
x1_0 = x1[:cut_point1]
x1_1 = x1[cut_point1:]
x2_0 = x2[:cut_point2]
x2_1 = x2[cut_point2:]
ac = fenzhi_multi(x1_0, x2_0)
bd = fenzhi_multi(x1_1, x2_1)
ad = fenzhi_multi(x1_0, x2_1)
bc = fenzhi_multi(x1_1, x2_0)
ac_10n = ac + '0' * 2 * cut_len
adbc = add(ad, bc) + '0' * cut_len
res = add(add(ac_10n, adbc), bd)
return res
def changed_fenzhi_multi(x1, x2):
if x1 == '0' or x2 == '0' or x1 == '' or x2 == '':
return '0'
sign = 1
if x1[0] == '-':
sign *= -1
x1 = x1[1:]
if x2[0] == '-':
sign *= -1
x2 = x2[1:]
l1 = len(x1)
l2 = len(x2)
if l1 == 1 or l2 == 1:
val = str(int(x1) * int(x2))
if sign == -1:
val = val[::-1] + '-'
val = val[::-1]
return val
else:
cut_len = int(min(l1, l2)/2)
cut_point1 = l1 - cut_len
cut_point2 = l2 - cut_len
x1_0 = x1[:cut_point1]
x1_1 = x1[cut_point1:]
x2_0 = x2[:cut_point2]
x2_1 = x2[cut_point2:]
ac = changed_fenzhi_multi(x1_0, x2_0)
bd = changed_fenzhi_multi(x1_1, x2_1)
a_add_b = add(x1_0, x1_1)
d_add_c = add(x2_1, x2_0)
abcd = changed_fenzhi_multi(a_add_b, d_add_c)
ac_10n = ac + '0'*2*cut_len
mid = sub1(sub1(abcd, ac), bd)
mid = mid + '0' * cut_len
val = add(add(ac_10n, mid), bd)
if sign == -1:
val = val[::-1] + '-'
val = val[::-1]
return val
if __name__ == '__main__':
for i in [10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000]:
a, b = number_generate(i)
print(len(a))
start_time = time.time()
res = fenzhi_multi(a, b)
end_time = time.time()
print("fenzhi:time:", end_time - start_time)
print('f', res)
print("s", simple_multi(a, b))
print('c', changed_fenzhi_multi(a, b))
折线图
import matplotlib.pyplot as plt
names = ['10', '100', '1000', '10000', '100000']
x1 = range(len(names))
y1 = [0.000044, 0.002929, 0.330447, 33.641515, 5678.60]
y2 = [0.000337, 0.031719, 3.543729, 300.875881, 3047.06]
y3 = [0.000533, 0.041108, 1.418264, 58.203367, 1902.112911]
# a normal scatter plot with default features
plt.plot(x1, y1, c='r', alpha=0.8, marker='o', label='simple')
plt.plot(x1, y2, c='g', alpha=0.8, marker='*', label='divide-and-conquer')
plt.plot(x1, y3, c='b', alpha=0.8, marker='+', label='changed divide-and-conquer')
plt.xticks(x1, names, rotation=45)
plt.xlabel('Number of digits')
plt.ylabel('Time')
plt.title('Multiplication of large integers')
plt.legend()
plt.show()