长整数运算

!!!!!

如果大家有更好的想法,或者发现了BUG,一定要告诉我哦。

非负整数运算

  这次,我们加法采用对位相加,也就是两个长整数对应位上的数字相加,之后再计算进位,最后得出结果的方法。

非负整数相加-对位相加

例如数字
被加数1111
加数99999

  先将数字串分割为独立的数字,再对位相加。

···十万万位千位百位十位个位
被加数001111
+++++++
加数099999
结果0910101010

  之后是处理进位。

···十万万位千位百位十位个位
个位进位09101010+1←0
十位进位091010+1←10
百位进位0910+1←110
千位进位010←1110
万位进位0+1←01110
最终结果101110

  上代码:

# 非负大整数相加
def SUM(NUM1, NUM2):
    # 分割数字字符串
    num1 = list(NUM1)
    num2 = list(NUM2)
    # 分割字符串从右对齐逐个相加存入temp
    len1 = len(num1)
    len2 = len(num2)
    count = max(len1, len2)
    index = 0
    temp = [0 for i in range(count+1)]
    if len1 > len2:  # 哪个数更长的操作
        index = len2-1
    else:
        index = len1-1
    for i in range(count, 0, -1):
        if len1 > len2:
            if index >= 0:
                temp[i] += int(num2[index])
                index -= 1
            temp[i] += int(num1[i-1])
        else:
            if index >= 0:
                temp[i] += int(num1[index])
                index -= 1
            temp[i] += int(num2[i-1])

    # 判断是否有进位
    for i in range(count, -1, -1):
        if temp[i] > 9:
            temp[i] = temp[i] % 10
            temp[i-1] += 1
        temp[i] = str(temp[i])

    # 判断结果是否有0前缀,有则去除
    while temp[0] == "0":
        if len(temp) == 1 and temp[0] == 0:
            return 0
        temp.pop(0)
        count -= 1

    # 大整数连接起来
    Sum = ''
    for i in range(count+1):
        Sum += temp[i]
    return Sum

  加数和被加数倒过来也一样,但是涉及到谁的位数更大一些,代码上需要判断一下,因为代码中保存对位相加结果的变量temp[]的长度是按最长的整数的长度而定的(多一位,因为可能会进位)。

非负整数相减-对位相减

  整数相减不同于相加,被减数和减数不能随意颠倒位置,但是我们仍可以使用对位相减的方法,只是需要限制。
  减法,实质上是求两个数的距离,例如:

9999 − 91111 = − ( 91111 − 9999 ) 9999 - 91111 = - ( 91111 - 9999 ) 999991111=(911119999)

  那么这样的话,我们可以将更大的数字始终作为被减数,只需要在结果判断是否添加负号即可。

例如数字
被减数9999
减数91111

  同加法,分割字符,但是减数更大,将减数变成被减数,被减数变为减数,结果添负:

···符号万位千位百位十位个位
被减数-91111
------
减数-9999
结果-9-8-8-8-8

  之后是处理借位。

···符号万位千位百位十位个位
结果-9-8-8-8-1→10-8
结果-9-8-8-1→10-92
结果-9-8-1→10-912
结果-9-1→10-9112
最终结果-81112

  那么问题来了! 能不能小的减大的呢,这不省掉了判断大小那一步吗?

  我们试一试:

···符号万位千位百位十位个位
被减数+9999
------
减数+91111
结果+-98888

  之后是处理借位,这里我发现一个规律,就是最高位是负的,需要向更高位借位,但是没有更高位了,头秃。

  但是,我发现将结果每位都取负,然后结果符号也取负。

  结果如下:

···符号万位千位百位十位个位
结果-9-8-8-8-8
最终结果-81112

  头秃,是可以的。但是也OK,代码也被简化了,上代码:

# 非负大整数相减,减法实质上是求两个数字的距离
def SUB(NUM1, NUM2):
    # 分割数字字符串
    num1 = list(NUM1)
    num2 = list(NUM2)
    # 分割字符串从右对齐长(大)减短(小)存入temp
    len1 = len(num1)
    len2 = len(num2)
    count = max(len1, len2)
    index = 0
    symbol = 0
    temp = [0 for i in range(count)]
    if len1 > len2:
        index = len2 - 1
    else:
        index = len1 - 1
    for i in range(count-1, -1, -1):
        if len1 > len2:
            if index >= 0:
                temp[i] -= int(num2[index])
                index -= 1
            temp[i] += int(num1[i])
        else:
            if index >= 0:
                temp[i] += int(num1[index])
                index -= 1
            temp[i] -= int(num2[i])

    # 判断结果是否有0前缀,有则去除
    while temp[0] == 0:
        if len(temp) == 1 and temp[0] == 0:
            return 0
        temp.pop(0)
        count -= 1

    # 符号置换,使最高位能够借位给低位
    if temp[0] < 0:
        symbol = 1
        for i in range(count):
            temp[i] = temp[i] * (-1)

    # 判断借位
    for i in range(count-1, -1, -1):
        if temp[i] < 0:
            temp[i] += 10
            temp[i-1] -= 1

    # 添加结果符号
    if symbol == 1:
        sub = "-"
        for i in range(count):
            sub += str(temp[i])
        return sub
    else:
        sub = ""
        for i in range(count):
            sub += str(temp[i])
        return sub

非负整数相乘

  整数相乘听上去很唬人,但是只要我们把我们平常使用的方法,转化为代码思想,其实不算难,只需注意几点就行。

  大家手算乘法的时候,是怎么算的呢?我觉得我们应该都一样,是用的这种方法

9 9 ∗ 9 9 9 8 9 1 8 9 1 8 9 1 = 9 8 9 0 1 \frac{\frac{\begin{matrix} &&&&&&&9&9\\ *&&&&&&9&9&9\end{matrix}}{\begin{matrix} &&&&&8&9&1\\ &&&&8&9&1\\ &&&8&9&1\end{matrix}}}{\begin{matrix}&=&&9&8&9&0&1&&\end{matrix}} =9890188989191199999

  那么把它转化为代码就简单了,我们同样的:

例如数字
被减数99
减数999

  分割字符,因为之前都有步骤,这里我就省略一些不必要的东西了:

···十万位万位千位百位十位个位
被乘数99
*
乘数999
步骤1:999中的最后一位的9与被乘数对位相乘结果8181
步骤1: 999中的中间位的9与被乘数对位相乘结果8181
步骤1: 999中的第一位9与被乘数对位相乘结果8181
步骤2: 各结果进位891
步骤2: 各结果进位891
步骤2.1:817101
步骤2.2: 2.1结果进位9801
步骤2: 各结果进位891
步骤2.1:818901
步骤2.1:2.1结果进位98901
最终结果98901

  这里大家看着可能会觉得有些乱,我解释一下:
    步骤2.1是步骤2后结果的对位相加(第一次步骤2也加了,但是是加到了0上面)
    为什么不等乘数所有的位数乘了被乘数后再对位相加和进位呢,举个例子:
8 9 1 8 9 1 8 9 1 \begin{matrix} &&&&&8&9&1\\ &&&&8&9&1\\ &&&8&9&1\end{matrix} 889891911
    在这之中,纵向拥有的数字最终都要加到一起,那么如果两个数字长度很大,那么
    这个纵向的项数就也会很大,那么使用原来的加法运算也没有意义了,因为可能结
    果就回到了长整数相加的问题上。所以我我们每计算两行,就把各列的数字加了,
    同时进位,那就把长整数相加的方法用到了这上面。

  当然,这样肯定不是最优,因为每算一列就要操作一次有点憨憨,如果有兴趣的话,可以自己优化一下,结合不同的数据类型可以定一个标准,即是最多算几列,再这样操纵一次。
  上代码:

# 非负大整数相乘
def MULTI(NUM1, NUM2):
    # 分割数字字符串
    num1 = list(NUM1)
    num2 = list(NUM2)
    len1 = len(num1)
    len2 = len(num2)
    count = max(len1, len2)
    temp = [0 for i in range(count*2)]

    ########例子#######
    #             999
    # *            999
    # -----------------
    #         81-81-81
    #      81-81-81
    #   81-81-81
    # ------------------
    #     9-9-8-0-0-1
    ########例子#######
    for i2 in range(len2-1, -1, -1):  # num2中的每位数从个位开始与num1相乘,最后结果按位相加到temp
        index1 = 0
        for i1 in range(len1-1, -1, -1):
            temp[count*2-(len2-i2)-index1] += int(num2[i2])*int(num1[i1])
            index1 += 1
        # 每算一次判断进位一次
        for i in range(count*2-1, -1, -1):
            if temp[i] > 9:
                temp[i-1] += int(temp[i] / 10)
                temp[i] = temp[i] % 10

    # 判断结果是否有0前缀,有则去除
    while temp[0] == 0:
        if len(temp) == 1 and temp[0] == 0:
            return 0
        temp.pop(0)
    # 返回结果
    multi = ""
    len_t = len(temp)
    for i in range(len_t):
        multi += str(temp[i])
    return multi

非负整数相除

  除法相比之下要复杂一点,因为涉及到了长整数的乘法和减法,但是之前我们已经做过了乘法和加法,我们现在只用调用就行了,思想还是比较简单的。
  我们用比较短的数字来了解一下除法的思想:

例如数字
被除数777
除数17

  基础的思想还是一样的分割字符,但是我们只用分割被除数,有必要的话,我们可以在小数点后面一直添加0来计算小数点后的位数:

···百位十位个位小数点后一位小数点后两位小数点后三位···
被除数777000
判断7
判断够减除数17吗17
结果0
判断77
判断够减除数17吗17
减后09
结果04
判断097
判断够减除数17吗17
减后012
结果045.
判断0120
判断够减除数17吗17
减后0001
结果045.7
判断00010
判断够减除数17吗17
结果045.70
判断000100
判断够减除数17吗17
减后000015
结果045.705
判断0000150
判断够减除数17吗17
减后0000014
结果045.7058

  记得在整数部分判断完的时候,结果加上小数点".",如果除不尽的话,这种循环会一直继续,不死不休,但是我们可以预先设置一个精度,即是精确到小数点后几位来阻止循环。
  这里我们需要注意一下,减法和判断能减几倍的除数的时候,我们需要用到之前写过的大整数的减法和乘法。
  我在这额外写了一个判断两个大整数大小的函数,对除法函数会很有帮助。
  上代码:

# 非负大整数相除
def DIV(NUM1, NUM2):
    # 分母为0
    if NUM2 == "0":
        assert 1/0
    # 分割数字字符串
    num1 = list(NUM1)
    num2 = list(NUM2)
    # 计算前将数字前缀0去除
    while num1[0] == "0":
        if num1[0] == "0" and len(num1) == 1:
            break
        num1.pop(0)
    while num2[0] == "0":
        if num2[0] == "0" and len(num2) == 1:
            break
        num2.pop(0)
    NUM1 = ""
    NUM2 = ""
    for i in range(len(num1)):
        NUM1 += num1[i]
    for i in range(len(num2)):
        NUM2 += num2[i]

    len1 = len(num1)
    len2 = len(num2)
    Comp = comp(NUM1, NUM2)
    count = max(len1, len2)
    if not (Comp == 0):
        temp = [NUM1[i] for i in range(count)]
        div = ""
        temp_NUM1 = ""
        for i in range(count):
            temp_NUM1 += temp[i]

            # 开减,被除数不够减一倍的除数的话向后移位,结果在对应位置添0
            # 如果够减一倍的,循环计算到来比较,最高到十倍,最多也只到十倍
            key1 = comp(temp_NUM1, NUM2)
            if key1 > 0:  # 够减一倍除数
                for i2 in range(2, 11, +1):
                    key2 = comp(temp_NUM1, MULTI(NUM2, str(i2)))
                    if key2 == 0:
                        temp_NUM1 = ""
                        div += str(i2)
                        break
                    if key2 < 0:
                        temp_NUM1 = SUB(temp_NUM1, MULTI(NUM2, str(i2-1)))
                        div += str(i2-1)
                        break
            if key1 == 0:  # 正好与一倍除数相等
                temp_NUM1 = ""
                div += "1"
            if key1 < 0:  # 不够,向后移动,结果添0
                div += "0"
        if not (temp_NUM1 == ""):
            # 小数精度accuracy
            print("整数除不尽,请输入需要小数精度:")
            try:
                accuracy = int(input())
                # 整数部分不够除,需要移位到小数点后
                for i in range(accuracy):
                    if i == 0:  # 结果添加小数点
                        div += "."
                    if temp_NUM1 == "":  # 能除尽,减完了结束
                        break
                    temp_NUM1 += "0"  # 向后移位
                    key1 = comp(temp_NUM1, NUM2)
                    if key1 > 0:
                        # 开减,思想与整数部分一致
                        for i2 in range(2, 11, +1):
                            key2 = comp(temp_NUM1, MULTI(NUM2, str(i2)))
                            if key2 == 0:
                                temp_NUM1 = ""
                                div += str(i2)
                                break
                            if key2 < 0:
                                temp_NUM1 = SUB(
                                    temp_NUM1, MULTI(NUM2, str(i2-1)))
                                div += str(i2-1)
                                break
                    if key1 == 0:  # 小数部分剪完,精度内能除尽
                        temp_NUM1 = ""
                        div += "1"
                        break
                    if key1 < 0:
                        div += "0"
            except Exception as e:
                print(e)
        # 去除前缀0
        temp_div = list(div)
        # 判断结果是否有0前缀,有则去除
        while temp_div[0] == "0" and not(temp_div[1] == "."):
            if len(temp_div) == 1 and temp_div[0] == 0:
                return 0
            temp_div.pop(0)
        div = ""
        index = len(temp_div)
        for i in range(index):
            div += temp_div[i]
        return div
    else:
        return 1

  这是判断两个非负长整数大小的函数:

# 比较大小 NUM1>NUM2:1 NUM1<NUM2:-1 NUM1==NUM2:0
def comp(NUM1, NUM2):#前缀0
    temp1 = list(NUM1)
    temp2 = list(NUM2)
    # 判断是否有0前缀,有则去除
    while temp1[0] == "0":
        if len(temp1) == 1 and temp1[0] == "0":
            break
        temp1.pop(0)
    while temp2[0] == "0":
        if len(temp2) == 1 and temp2[0] == "0":
            break
        temp2.pop(0)
    len1 = len(temp1)
    len2 = len(temp2)
    if len1 > len2:
        return 1
    if len1 < len2:
        return -1
    if len1 == len2:
        for i in range(len1):
            if NUM1[i] > NUM2[i]:
                return 1
            if NUM1[i] < NUM2[i]:
                return -1
            if NUM1[i] == NUM2[i] and i == len1 - 1:
                return 0

整数运算

  带符号的运算其实很简单,只要不带符号的搞懂了,带符号的只是调用之前的方法再判断需不需要补一个负号即可。
  我在这简单解释一下之后的参数,都用的同一组参数:

    参数(NUM1:去掉符号后的被加数,NUM2:去掉符号后的加数,Sym1:被加数的符号,Sym2:加数的符号)
  NUM类型为str,在传入之前如果前缀带有"-",则用"0"替换,只替换1次
  Sym类型为int,1/-1分别对应"+"/"-"

大整数相加

  带符号的加法,其实很简单,只需判断一下符号,调用之前写好的函数就行啦。
  由于很简单,直接上代码:

# 大整数相加
def USUM(NUM1, NUM2, Sym1, Sym2):
    if(Sym1*Sym2 > 0):
        if Sym1 > 0:
            return SUM(NUM1, NUM2)
        else:
            return "-" + SUM(NUM1, NUM2)
    else:
        if Sym1 > 0:
            return SUB(NUM1, NUM2)
        else:
            return SUB(NUM2, NUM1)

大整数相减

  带符号的减法和加法一样:

# 大整数相减
def USUB(NUM1, NUM2, Sym1, Sym2):
    if(Sym1*Sym2 > 0):
        if Sym1 > 0:
            return SUB(NUM1, NUM2)
        else:
            return SUB(NUM2, NUM1)
    else:
        if Sym1 > 0:
            return SUM(NUM1, NUM2)
        else:
            return "-" + SUM(NUM1, NUM2)

大整数相乘

  带符号的乘法更简单:

#大整数相除
def UMULTI(NUM1, NUM2, Sym1, Sym2):
    if(Sym1*Sym2 > 0):
        return MULTI(NUM1, NUM2)
    else:
        return "-" + MULTI(NUM1, NUM2)

大整数相除

  除法搞了,这个简直不要太简单:

# 大整数相除
def UDIV(NUM1, NUM2, Sym1, Sym2):
    if(Sym1*Sym2 > 0):
        return DIV(NUM1, NUM2)
    else:
        return "-" + DIV(NUM1, NUM2)
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值