以字符串形式输入两个正整数,计算“两数相乘”(不可使用Biginteage等内置库),几种“变相”算法求解。
-
Python 官网:https://www.python.org/
-
Free:大咖免费“圣经”教程《 python 完全自学教程》,不仅仅是基础那么简单……
地址:https://lqpybook.readthedocs.io/
自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长,没有老师的时候总是比有老师的时候多。
—— 华罗庚
![等风来,不如追风去……](https://i-blog.csdnimg.cn/blog_migrate/4a8b43fa255f3b54c491f1a1b49b890d.jpeg)
本文质量分:
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 秒。
- 让QQ群昵称色变的神奇代码
( 51985 阅读)
点赞:22 踩:0 打赏:0 评论:16
本篇博文笔记于 2022-01-18 19:15:08 首发,最晚于 2022-01-20 07:56:47 修改。 - ChatGPT国内镜像站初体验:聊天、Python代码生成等
( 40292 阅读)
点赞:120 踩:0 打赏:0 评论:73
本篇博文笔记于 2023-02-14 23:46:33 首发,最晚于 2023-03-22 00:03:44 修改。 - pandas 数据类型之 DataFrame
( 7478 阅读)
点赞:6 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-05-01 13:20:17 首发,最晚于 2022-05-08 08:46:13 修改。 - 罗马数字转换器|罗马数字生成器
( 6149 阅读)
点赞:0 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-01-19 23:26:42 首发,最晚于 2022-01-21 18:37:46 修改。 - Python字符串居中显示
( 5664 阅读)
点赞:1 踩:0 打赏:0 评论:1
本篇博文笔记于 2021-12-26 23:35:29 发布。 - 斐波那契数列的递归实现和for实现
( 5190 阅读)
点赞:4 踩:0 打赏:0 评论:8
本篇博文笔记于 2022-01-06 23:27:40 发布。 - 练习:字符串统计(坑:f‘string‘报错)
( 4767 阅读)
点赞:0 踩:0 打赏:0 评论:0
本篇博文笔记于 2021-12-04 22:54:29 发布。 - 练习:尼姆游戏(聪明版/傻瓜式•人机对战)
( 4465 阅读)
点赞:14 踩:0 打赏:0 评论:0
本篇博文笔记于 2021-11-30 23:43:17 发布。 - 回车符、换行符和回车换行符
( 4159 阅读)
点赞:0 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-02-24 13:10:02 首发,最晚于 2022-02-25 20:07:40 修改。 - python清屏
( 3887 阅读)
点赞:0 踩:0 打赏:0 评论:0
本篇博文笔记于 2021-10-14 13:47:21 发布。 - 密码强度检测器
( 3710 阅读)
点赞:1 踩:0 打赏:0 评论:0
本篇博文笔记于 2021-12-06 09:08:25 首发,最晚于 2022-11-27 09:39:39 修改。 - 罗马数字转换器(用罗马数字构造元素的值取模实现)
( 3679 阅读)
点赞:0 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-01-20 19:38:12 首发,最晚于 2022-01-21 18:32:02 修改。 - 个人信息提取(字符串)
( 3530 阅读)
点赞:1 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-04-18 11:07:12 首发,最晚于 2022-04-20 13:17:54 修改。 - 练习:班里有人和我同生日难吗?(概率probability、蒙特卡洛随机模拟法)
( 3424 阅读)
点赞:1 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-04-26 12:46:25 首发,最晚于 2022-04-27 21:22:07 修改。 - 练习:生成100个随机正整数
( 3195 阅读)
点赞:1 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-01-18 13:31:36 首发,最晚于 2022-01-20 07:58:12 修改。 - 我的 Python.color() (Python 色彩打印控制)
( 3094 阅读)
点赞:2 踩:0 打赏:0 评论:0
本篇博文笔记于 2022-02-28 22:46:21 首发,最晚于 2022-03-03 10:30:03 修改。 - 练习:仿真模拟福彩双色球——中500w巨奖到底有多难?跑跑代码就晓得了。
( 3003 阅读)
点赞:3 踩:0 打赏:0 评论:3
本篇博文笔记于 2022-06-22 19:54:20 首发,最晚于 2022-06-23 22:41:33 修改。
![老齐漫画头像](https://i-blog.csdnimg.cn/blog_migrate/8cbf2c45d8b57205b31ef4963b3b9854.jpeg)
精品文章:
- 好文力荐:齐伟书稿 《python 完全自学教程》 Free连载(已完稿并集结成书,还有PDF版本百度网盘永久分享,点击跳转免费🆓下载。)
- OPP三大特性:封装中的property
- 通过内置对象理解python'
- 正则表达式
- python中“*”的作用
- Python 完全自学手册
- 海象运算符
- Python中的 `!=`与`is not`不同
- 学习编程的正确方法
来源:老齐教室
◆ Python 入门指南【Python 3.6.3】
好文力荐:
-
全栈领域优质创作者——寒佬(还是国内某高校学生)博文“非技术文—关于英语和如何正确的提问”,“英语”和“会提问”是学习的两大利器。
CSDN实用技巧博文: