字符串相乘(不可使用Biginteage类内置库),几种“变相”算法求解

75 篇文章 0 订阅

以字符串形式输入两个正整数,计算“两数相乘”(不可使用Biginteage等内置库),几种“变相”算法求解。


【学习的细节是欢悦的历程】


  自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长,没有老师的时候总是比有老师的时候多。
            —— 华罗庚


等风来,不如追风去……


以字符串形式输入两个正整数
字符串相乘
(计算“两数相乘”,不使用Biginteage等内置库)


本文质量分:

90
本文地址: https://blog.csdn.net/m0_57158496/article/details/129869005

CSDN质量分查询入口:http://www.csdn.net/qc


目 录


◆字符串相乘


1、题目描述


  给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。


样例 1:
输入:
num1 = “2”, num2 = “3”

输出:
“6”


样例 2:
输入:
num1 = “123”, num2 = “456”

输出:
“56088”


提示:

  • 1 <= num1.length, num2.length <= 200
  • num1 和 num2 只能由数字组成。
  • num1 和 num2 都不包含任何前导零,除了数字0本身。


回页目录

2、佬的代码

2.1 佬的“原”码


  喜阅博文,偶见代码清奇,CV录之。


class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        if num1 == "0" or num2 == "0":
            return "0"

        m, n = len(num1), len(num2)
        ans_arr = [0] * (m + n)
        for i in range(m - 1, -1, -1):
            x = int(num1[i])
            for j in range(n - 1, -1, -1):
                ans_arr[i + j + 1] += x * int(num2[j])

        for i in range(m + n - 1, 0, -1):
            ans_arr[i - 1] += ans_arr[i] // 10
            ans_arr[i] %= 10

        index = 1 if ans_arr[0] == 0 else 0
        ans = "".join(str(x) for x in ans_arr[index:])
        return ans

2.2 品读佬的代码


class Solution:
    def multiply(self, num1: str, num2: str) -> str:

        if num1 == "0" or num2 == "0":
            return "0" # 两数其一为“0”,乘积为“0”。

        m, n = len(num1), len(num2)
        ans_arr = [0] * (m + n) # 乘积数位列表,预调乘积数位 len(num1) + len(num2)位。

        for i in range(m - 1, -1, -1): # 两层for嵌套求乘积各位数值。
            x = int(num1[i])
            print()
            for j in range(n - 1, -1, -1):
                print(x * int(num2[j]))
                ans_arr[i + j + 1] += x * int(num2[j])

        print(ans_arr)
        
        for i in range(m + n - 1, 0, -1): # 处理各数位进位,得到积的各数位数字。
            ans_arr[i - 1] += ans_arr[i] // 10 # 向前进位。
            ans_arr[i] %= 10 # 模10得到本数组数字。

        print(ans_arr)
        index = 1 if ans_arr[0] == 0 else 0 # 如果最高位是零,舍去高位零。
        ans = "".join(str(x) for x in ans_arr[index:]) # 拼接非高位“9”外的数位数字(乘积)。
        return ans

在这里插入图片描述
在这里插入图片描述

  通过品读佬的代码,且用print()让代码执行过程显形,才完全理解佬的算法:收集两数乘积各数位上的乘积之和,再遍历进位后得到乘积的各数位上的数字,’’.join()拼接输出乘积整数字符串。这算法也算是别具一格了。

2.3 复刻佬的算法


  通透了佬的算法,我手痒痒地开启了“复刻模式”😄
  我用dict来收集数位乘积,不用预先设备位置,这一点比list好使,dict.get(key, 0)获取dict中key的值进行累加,key不在dict中,则创建key其值赋“0”;用str[i-1]或者list.pop()来让字符串整数颠倒数位,高位数字在后低位在前,方便遍历;用enumerate()来简洁遍历时的下标引用(enumerate可以同时遍历下标和元素)
在这里插入图片描述
  题目限定不可以用Biginteger类内置库,且不可以将输入整数字符串直接转换成整型int相乘,也就是要求“手撕”多位数乘法,但并未限定最后结果输出字符串不可以用str转换函数。所以——

  • 直接str()转换最后乘积为字符串输出

class Solution:

    def multiply(self, num1: str, num2: str) -> str:

        if num1 == "0" or num2 == "0":
            return "0" # 两数其一为“0”,乘积为“0”。

        num1 = [num1[i-1] for i in range(len(num1), 0, -1)] # 用下标实现整数低位在前。
        num2 = list(num2)
        num2 = [num2.pop() for i in num2[:]] # 用list.pop()方法模拟堆栈弹出实现整数低位在前。
        #input((num1, num2)) # 调试代码显示变量语句。
        ans_arr = {} # 乘积数位字典。
        
        for m,i in enumerate(num1):
            for n,j in enumerate(num2):
                print(m + n, int(i) * int(j)) # 调试用语句,非必需,可以注释掉。
                ans_arr[m + n] = ans_arr.get(m + n, 0) + int(i) * int(j)

        print(f"\n乘积各数位字典(0为个位,1为十位,2为百位……):\n{ans_arr}") # 调试用语句,非必需,可以注释掉。
        
        return str(sum(value * (10**key) for key,value in ans_arr.items())) # 列表解析出乘积各数位值,求和后转str返回。


  • 进位取得最终乘积数位数字列表ans,拼接输出字符串

class Solution:

    def multiply(self, num1: str, num2: str) -> str:

        if num1 == "0" or num2 == "0":
            return "0" # 两数其一为“0”,乘积为“0”。

        num1 = [num1[i-1] for i in range(len(num1), 0, -1)] # 用下标实现整数低位在前。
        num2 = list(num2)
        num2 = [num2.pop() for i in num2[:]] # 用list.pop()方法模拟堆栈弹出实现整数低位在前。
        #input((num1, num2)) # 调试代码显示变量语句。
        ans_arr = {} # 乘积数位字典。
        
        for m,i in enumerate(num1):
            for n,j in enumerate(num2):
                print(m + n, int(i) * int(j)) # 调试用语句,非必需,可以注释掉。
                ans_arr[m + n] = ans_arr.get(m + n, 0) + int(i) * int(j)

        print(f"\n乘积各数位字典(0为个位,1为十位,2为百位……):\n{ans_arr}") # 调试用语句,非必需,可以注释掉。
        
        ans = []
        for key,value in ans_arr.items():
            #print(value//10) 代码调试用语句。
            if value//10:
                ans_arr[key + 1] = ans_arr.get(key+1, 0) + value//10
                ans.insert(0, str(value%10))
            else:
                ans.insert(0, str(value%10))

            #print(ans_arr) 代码调试用语句。

        #input(ans) #代码调试用语句。
        return ''.join(ans) # 列表解析出乘积各数位值,求和后转str返回。


回页目录

3、我的“变相”算法


  前面代码的主人在博文中提到,“手撕”多位数乘法,实则是模拟我们在学校学到的列竖式计算。那,除了佬的算法外,还可以有“变相”算法:

3.1 “竖式”直算


  我们的传统竖式,如果得到的数位乘积不进行进位操作,会是下图的样子。
在这里插入图片描述据上图原理进行两层for遍历,


    for i in range(len(num1)-1, -1, -1):

        multiple2 = 1 # num2的数位等差初值。
        for j in range(len(num1)-1, -1, -1):
            print(int(num1[i])*int(num2[j])*multiple1*multiple2)
            out += int(num1[i])*int(num2[j])*multiple1*multiple2 # 乘积累加。
            multiple2 *= 10 # num2的数位等差自增。
            #print(num1[i], num2[j]) # 调试用语句。

        multiple1 *= 10 # num1的数位等差自增。

即得示例123×456的九次相乘积的列表,
在这里插入图片描述
变量累加或者sum(遍历乘积集合),就得到最后的两个多位整数相乘之积。

  • 负数下标索引(倒序遍历),常规之术,易于理解。

变量累加各分项计算乘积


def str_multiply(num1: str, num2: str) -> str:
    ''' 字符串相乘 '''
    out_first = f"\n{'':>8}{num1} × {num2} = "
    out = 0 # 乘积初值。
    multiple1 = 1 # num1的数位等差初值。

    for i in range(len(num1)-1, -1, -1):

        multiple2 = 1 # num2的数位等差初值。
        for j in range(len(num1)-1, -1, -1):
            print(int(num1[i])*int(num2[j])*multiple1*multiple2)
            out += int(num1[i])*int(num2[j])*multiple1*multiple2 # 乘积累加。
            multiple2 *= 10 # num2的数位等差自增。
            #print(num1[i], num2[j]) # 调试用语句。

        multiple1 *= 10 # num1的数位等差自增。
    
    return f"\n{out_first}{out}"


  • 先反转整数字数字符,再常规下标索引遍历。正常遍历,代码简洁,明了易读。

在这里插入图片描述
sum()汇总列表分步计算乘积列表


def str_multiply4(num1: str, num2: str) -> str:
    ''' 字符串相乘 '''
    out_first = f"\n{'':>8}{num1} × {num2} = "
    out = [] # 两数乘积分项列表。
    num1, num2 = list(num1), list(num2)
    # 列表解析整数数位倒置。
    num1 = ''.join([num1.pop() for i in num1[:]])
    num2 = ''.join([num2.pop() for i in num2[:]])

    multiple1 = 1 # num1的数位等差初值。
    for i in num1:

        multiple2 = 1 # num2的数位等差初值。
        for j in num2:
            out.append(int(i)*int(j)*multiple1*multiple2)
            multiple2 *= 10 # num2的数位等差自增。
            #print(i, j) # 调试代码语句打印。
        
        multiple1 *= 10 # num1的数位等差自增。
    
    
    print(f"\n分项乘积列表:\n{out}") # 调试代码用显示变量。
    out = sum(out)
    if not out%10: # 乘积为一位数,直接返回乘积。
        return f"{first}{out}\n"
    
    out_s, n = [], 1
    
    while out//(10**n): # 逐位抽取多倍整数的各位数数字。

        if n == 1:
            out_s.insert(0, str(out%10)) # 取个位。

        out_s.insert(0, str(out//(10**n)%10)) # 取十、百、千、万……直到数位全部取完。
        n += 1
        
    #input(out_s) # 调试代码用显示变量语句。
    return f"\n{out_first}{''.join(out_s)}\n" # 拼接各位数字字符返回乘积数字字符串。

3.2 传统竖式


  累加内层for得到的乘积,即得到传统竖式计算多位数乘法的一致算法。

在这里插入图片描述
在这里插入图片描述


def str_multiply3(num1: str, num2: str) -> str:
    ''' 字符串相乘 '''
    out_first = f"\n{'':>8}{num1} × {num2} = "
    multiple = {} # 乘积字典,以整数数位等差级别为key。
    num1, num2 = list(num1), list(num2)
    # 列表解析整数数位倒置。
    num1 = ''.join([num1.pop() for i in num1[:]])
    num2 = ''.join([num2.pop() for i in num2[:]])

    place1 = 1 # 个位数位等差级别。
    for i in num1:
        
        place2 = 1
        for j in num2:
            multiple[place1] = multiple.get(place1, 0) + int(i) * int(j) * place2 # 数位等差级别key(累加数位乘积)。
            place2 *= 10 # num2数位等差级别变高一级。
    
        place1 *= 10 # num1数位等差级别变高一级。

    print(f"\n数位乘积分项字典:\n{multiple}")
    return f"{out_first}{sum([key*value for key,value in multiple.items()])}\n" # 列表解析出乘积求和返回。


回页目录

4、完整源码

(源码较长,点此跳过源码)

#!/sur/bin/nve python
# coding: utf-8

'''

字符串相乘

'''


# CV佬的代码
class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        if num1 == "0" or num2 == "0":
            return "0"

        m, n = len(num1), len(num2)
        ans_arr = [0] * (m + n)
        for i in range(m - 1, -1, -1):
            x = int(num1[i])
            for j in range(n - 1, -1, -1):
                ans_arr[i + j + 1] += x * int(num2[j])

        for i in range(m + n - 1, 0, -1):
            ans_arr[i - 1] += ans_arr[i] // 10
            ans_arr[i] %= 10

        index = 1 if ans_arr[0] == 0 else 0
        ans = "".join(str(x) for x in ans_arr[index:])
        return ans


# 品读佬的代码
class Solution:
    def multiply(self, num1: str, num2: str) -> str:

        if num1 == "0" or num2 == "0":
            return "0" # 两数其一为“0”,乘积为“0”。

        m, n = len(num1), len(num2)
        ans_arr = [0] * (m + n) # 乘积数位列表,预调乘积数位 len(num1) + len(num2)位。

        for i in range(m - 1, -1, -1): # 两层for嵌套求乘积各位数值。
            x = int(num1[i])
            print()
            for j in range(n - 1, -1, -1):
                print(x * int(num2[j]))
                ans_arr[i + j + 1] += x * int(num2[j])

        print(ans_arr)
        
        for i in range(m + n - 1, 0, -1): # 处理各数位进位,得到积的各数位数字。
            ans_arr[i - 1] += ans_arr[i] // 10 # 向前进位。
            ans_arr[i] %= 10 # 模10得到本数组数字。

        print(ans_arr)
        index = 1 if ans_arr[0] == 0 else 0 # 如果最高位是零,舍去高位零。
        ans = "".join(str(x) for x in ans_arr[index:]) # 拼接非高位“9”外的数位数字(乘积)。
        return ans


# 按佬的“思路”改写Python代码
class Solution:

    def multiply(self, num1: str, num2: str) -> str:

        if num1 == "0" or num2 == "0":
            return "0" # 两数其一为“0”,乘积为“0”。

        num1 = [num1[i-1] for i in range(len(num1), 0, -1)] # 用下标实现整数低位在前。
        num2 = list(num2)
        num2 = [num2.pop() for i in num2[:]] # 用list.pop()方法模拟堆栈弹出实现整数低位在前。
        #input((num1, num2)) # 调试代码显示变量语句。
        ans_arr = {} # 乘积数位字典。
        
        for m,i in enumerate(num1):
            for n,j in enumerate(num2):
                print(m + n, int(i) * int(j)) # 调试用语句,非必需,可以注释掉。
                ans_arr[m + n] = ans_arr.get(m + n, 0) + int(i) * int(j)

        print(f"\n乘积各数位字典(0为个位,1为十位,2为百位……):\n{ans_arr}") # 调试用语句,非必需,可以注释掉。
        
        #return str(sum(value * (10**key) for key,value in ans_arr.items())) # 列表解析出乘积各数位值,求和后转str返回。

        ans = []
        for key,value in ans_arr.items():
            #print(value//10) 代码调试用语句。
            if value//10:
                ans_arr[key + 1] = ans_arr.get(key+1, 0) + value//10
                ans.insert(0, str(value%10))
            else:
                ans.insert(0, str(value%10))

            #print(ans_arr) 代码调试用语句。

        #input(ans) #代码调试用语句。
        return ''.join(ans) # 列表解析出乘积各数位值,求和后转str返回。


def str_multiply(num1: str, num2: str) -> str:
    ''' 字符串相乘 '''
    out_first = f"\n{'':>8}{num1} × {num2} = "
    out = 0 # 乘积初值。
    multiple1 = 1 # num1的数位等差初值。

    for i in range(len(num1)-1, -1, -1):

        multiple2 = 1 # num2的数位等差初值。
        for j in range(len(num1)-1, -1, -1):
            print(int(num1[i])*int(num2[j])*multiple1*multiple2)
            out += int(num1[i])*int(num2[j])*multiple1*multiple2 # 乘积累加。
            multiple2 *= 10 # num2的数位等差自增。
            #print(num1[i], num2[j]) # 调试用语句。

        multiple1 *= 10 # num1的数位等差自增。
    
    return f"\n{out_first}{out}"


def str_multiply(num1: str, num2: str) -> str:
    ''' 字符串相乘 '''
    out_first = f"\n{'':>8}{num1} × {num2} = "
    out = [] # 两数乘积分项列表。
    num1, num2 = list(num1), list(num2)
    # 列表解析整数数位倒置。
    num1 = ''.join([num1.pop() for i in num1[:]])
    num2 = ''.join([num2.pop() for i in num2[:]])

    multiple1 = 1 # num1的数位等差初值。
    for i in num1:

        multiple2 = 1 # num2的数位等差初值。
        for j in num2:
            out.append(int(i)*int(j)*multiple1*multiple2)
            multiple2 *= 10 # num2的数位等差自增。
            #print(i, j) # 调试代码语句打印。
        
        multiple1 *= 10 # num1的数位等差自增。
    
    
    print(f"\n分项乘积列表:\n{out}") # 调试代码用显示变量。
    out = sum(out)
    if not out%10: # 乘积为一位数,直接返回乘积。
        return f"{first}{out}\n"
    
    out_s, n = [], 1
    
    while out//(10**n): # 逐位抽取多倍整数的各位数数字。

        if n == 1:
            out_s.insert(0, str(out%10)) # 取个位。

        out_s.insert(0, str(out//(10**n)%10)) # 取十、百、千、万……直到数位全部取完。
        n += 1
        
    #input(out_s) # 调试代码用显示变量语句。
    return f"\n{out_first}{''.join(out_s)}\n" # 拼接各位数字字符返回乘积数字字符串。


def str_multiply(num1: str, num2: str) -> str:
    ''' 字符串相乘 '''
    out_first = f"\n{'':>8}{num1} × {num2} = "
    multiple = {} # 乘积字典,以整数数位等差级别为key。
    num1, num2 = list(num1), list(num2)
    # 列表解析整数数位倒置。
    num1 = ''.join([num1.pop() for i in num1[:]])
    num2 = ''.join([num2.pop() for i in num2[:]])

    place1 = 1 # 个位数位等差级别。
    for i in num1:
        
        place2 = 1
        for j in num2:
            multiple[place1] = multiple.get(place1, 0) + int(i) * int(j) * place2 # 数位等差级别key(累加数位乘积)。
            place2 *= 10 # num2数位等差级别变高一级。
    
        place1 *= 10 # num1数位等差级别变高一级。

    print(f"\n数位乘积分项字典:\n{multiple}")
    return f"{out_first}{sum([key*value for key,value in multiple.items()])}\n" # 列表解析出乘积求和返回。


if __name__ == '__main__':
    sol = Solution()
    print(sol.multiply('123', '456'))
    print(str_multiply('5', '3'))
    print(str_multiply('123', '456'))


回页首

__上一篇:__ 我的CSDN博文笔记总索引(Python代码自动降序“阅读量”编排)(129728550)
__下一篇:__ 

我的HOT博:


  本次共计收集 188 篇博文笔记信息,平均阅读量 1517 。已生成阅读量大于3000的 17 篇笔记索引链接。数据采集于 2023-03-30 22:27:21 完成,用时 4 分 57.30 秒。


  1. 让QQ群昵称色变的神奇代码
    ( 51985 阅读)
    点赞:22  踩:0  打赏:0  评论:16
    本篇博文笔记于 2022-01-18 19:15:08 首发,最晚于 2022-01-20 07:56:47 修改。
  2. ChatGPT国内镜像站初体验:聊天、Python代码生成等
    ( 40292 阅读)
    点赞:120  踩:0  打赏:0  评论:73
    本篇博文笔记于 2023-02-14 23:46:33 首发,最晚于 2023-03-22 00:03:44 修改。
  3. pandas 数据类型之 DataFrame
    ( 7478 阅读)
    点赞:6  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-05-01 13:20:17 首发,最晚于 2022-05-08 08:46:13 修改。
  4. 罗马数字转换器|罗马数字生成器
    ( 6149 阅读)
    点赞:0  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-01-19 23:26:42 首发,最晚于 2022-01-21 18:37:46 修改。
  5. Python字符串居中显示
    ( 5664 阅读)
    点赞:1  踩:0  打赏:0  评论:1
    本篇博文笔记于 2021-12-26 23:35:29 发布。
  6. 斐波那契数列的递归实现和for实现
    ( 5190 阅读)
    点赞:4  踩:0  打赏:0  评论:8
    本篇博文笔记于 2022-01-06 23:27:40 发布。
  7. 练习:字符串统计(坑:f‘string‘报错)
    ( 4767 阅读)
    点赞:0  踩:0  打赏:0  评论:0
    本篇博文笔记于 2021-12-04 22:54:29 发布。
  8. 练习:尼姆游戏(聪明版/傻瓜式•人机对战)
    ( 4465 阅读)
    点赞:14  踩:0  打赏:0  评论:0
    本篇博文笔记于 2021-11-30 23:43:17 发布。
  9. 回车符、换行符和回车换行符
    ( 4159 阅读)
    点赞:0  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-02-24 13:10:02 首发,最晚于 2022-02-25 20:07:40 修改。
  10. python清屏
    ( 3887 阅读)
    点赞:0  踩:0  打赏:0  评论:0
    本篇博文笔记于 2021-10-14 13:47:21 发布。
  11. 密码强度检测器
    ( 3710 阅读)
    点赞:1  踩:0  打赏:0  评论:0
    本篇博文笔记于 2021-12-06 09:08:25 首发,最晚于 2022-11-27 09:39:39 修改。
  12. 罗马数字转换器(用罗马数字构造元素的值取模实现)
    ( 3679 阅读)
    点赞:0  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-01-20 19:38:12 首发,最晚于 2022-01-21 18:32:02 修改。
  13. 个人信息提取(字符串)
    ( 3530 阅读)
    点赞:1  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-04-18 11:07:12 首发,最晚于 2022-04-20 13:17:54 修改。
  14. 练习:班里有人和我同生日难吗?(概率probability、蒙特卡洛随机模拟法)
    ( 3424 阅读)
    点赞:1  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-04-26 12:46:25 首发,最晚于 2022-04-27 21:22:07 修改。
  15. 练习:生成100个随机正整数
    ( 3195 阅读)
    点赞:1  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-01-18 13:31:36 首发,最晚于 2022-01-20 07:58:12 修改。
  16. 我的 Python.color() (Python 色彩打印控制)
    ( 3094 阅读)
    点赞:2  踩:0  打赏:0  评论:0
    本篇博文笔记于 2022-02-28 22:46:21 首发,最晚于 2022-03-03 10:30:03 修改。
  17. 练习:仿真模拟福彩双色球——中500w巨奖到底有多难?跑跑代码就晓得了。
    ( 3003 阅读)
    点赞:3  踩:0  打赏:0  评论:3
    本篇博文笔记于 2022-06-22 19:54:20 首发,最晚于 2022-06-23 22:41:33 修改。
推荐条件 阅读量突破三千
(更多热博,请点击蓝色文字跳转翻阅)

回页首


老齐漫画头像

精品文章:

来源:老齐教室


回页首

Python 入门指南【Python 3.6.3】


好文力荐:


CSDN实用技巧博文:

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

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

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

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    梦幻精灵_cq

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值