python-大整数相乘-分治法

暴力相加

下面展示一些 内联代码片

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.0000440.00033712387084960940.000533342361450
100位0.0029292106628410.031719446182250980.04110884666442871
1000位0.33044719696044923.5437297821044921.418264627456665
10000位33.64151573181152300.875881671905558.203367471694946
100000位5678.6030478.06856727656603.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()
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值