Python世界:力扣题43大数相乘算法实践

Python世界:力扣题43大数相乘算法实践

    • 任务背景
    • 思路分析
      • 方案1
      • 方案2
      • 方案3
      • 方案4
      • 无测试套主调
      • 测试套主调
    • 本文小结

任务背景


问题来自力扣题目43:字符串相乘,大意如下:

Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string.

翻译下,需求是:实现大数相乘,字符串乘法

  1. 输入为非负整数两个字符串
  2. 要求输出该大数值的乘积

思路分析


方案1

自然的想法是,模拟乘法运算,考验对实际问题的计算机转换,先手动模拟下计算过程,提炼其中算法,如果最高位相乘及低位相加无累进,则提前退出。

99*99=9801  2*2=4
10*10=100   2*2=3

以下示例,运行时间击败32%:

# sol1:暴力法遍历
class Solution:def multiply(self, num1: str, num2: str) -> str:# corner caseif num1 == "0" or num2 == "0":return "0"elif num1 == "1":return num2elif num2 == "1":return num1# common caselen1 = len(num1)len2 = len(num2)len_sum = len1 + len2len_max = max(len1, len2)len_min = min(len1, len2)# 从低位往高位相互进位,个、十、百、千、……n1_rev = num1[::-1]n2_rev = num2[::-1]multi_res_list = []base = 10c = 0 # carrier# 暴力法for b in range(len_sum + 1):val = 0res = 0# 获取一个阶的结果,如百、十、千for i in range(len1):if i > b or b - i >= len2: # i,j比目标进位大,已到头continuej = b - i # j>=0 && j<len2n1 = int(n1_rev[i])n2 = int(n2_rev[j])res += n1 * n2# 处理一个阶的结果res += cc = res // baseval = res - c * baseassert(val < base)if (c == 0 and val == 0 and b > len_max): # 去除冗余前导零continuemulti_res_list.append(str(val))# 将列表逆序并转化为字符串输出# multi_res_list = multi_res_list.reverse() # 未按预期运行,输出结果为Nonemulti_res_list = multi_res_list[::-1]non_zero_idx = 0for val in multi_res_list:if (val == "0"):non_zero_idx += 1else:breakmulti_res_list = multi_res_list[non_zero_idx:]multi_res_str = "".join(multi_res_list) # 列表转字符串return multi_res_str

方案2

尝试进一步改进:

  • 通过限制上下界,降低内外for循环次数
    • 内循环len1选两者较小的长度
    • 如果i大于b时,直接break
    • 外循环b设计提前退出条件,当前导都是零时,无计算必要
  • 不整体逆序,直接从末尾字符低位往高位移动(TBD)
# sol2:beat 42.5%
class Solution:def multiply(self, num1: str, num2: str) -> str:# corner caseif num1 == "0" or num2 == "0":return "0"elif num1 == "1":return num2elif num2 == "1":return num1# common caselen1 = len(num1)len2 = len(num2)len_sum = len1 + len2len_max = max(len1, len2)len_min = min(len1, len2)# 从低位往高位相互进位,个、十、百、千、……n1_rev = num1[::-1]n2_rev = num2[::-1]multi_res_list = []base = 10c = 0 # carrier# 暴力法for b in range(len_sum): # b [0, len_sum-1]val = 0res = 0# 获取一个阶的结果,如百、十、千for i in range(len1):if i > b:breakif b - i >= len2: # i,j比目标进位大,已到头continuej = b - i # j>=0 && j<len2n1 = int(n1_rev[i])n2 = int(n2_rev[j])res += n1 * n2# 处理一个阶的结果res += cc = res // baseval = res - c * baseassert(val < base)if (c == 0 and val == 0 and b > len_max): # 去除冗余前导零continuemulti_res_list.append(str(val))if (b + 1 == len_sum and c == 0):break # 最高位相乘无进位# 将列表逆序并转化为字符串输出# multi_res_list = multi_res_list.reverse() # 未按预期运行,输出结果为Nonemulti_res_list = multi_res_list[::-1]non_zero_idx = 0for val in multi_res_list:if (val == "0"):non_zero_idx += 1else:breakmulti_res_list = multi_res_list[non_zero_idx:]multi_res_str = "".join(multi_res_list) # 列表转字符串return multi_res_str

方案3

网上参考的一种实现,运行时间对比:

# # sol3:beat 29.9%
# # 参考解法:https://blog.csdn.net/huqinweI987/article/details/88797663
class Solution:def multiply(self, num1: str, num2: str) -> str:if num1 == '0' or num2 == '0':#有0就不用乘了。return '0'res = ''carry = 0#初始化# 两个数的长度,分别都减1m = len(num1) - 1n = len(num2) - 1# m和n都是len减1,是因为,15*15中,不算被动进位,能用来主动计算乘法的,最高位就是百位,10*10=100,是主动计算的最高位。# k就在[0,m+n]的区间:代表主动计算乘法的位(最后多出来的进位单独给出)。k=0,i和j都是0,5*5,对应个位结果。# k=1,i和j分别是0、1和1、0组合,是10和5或者5和10,对应十位的结果,# k=2,i和j分别是1、1(其他组合不满足筛选条件,我计算的就是百位,不能把5也拿来用吧,把乘法写一下就出来了),代表10和10相乘,对应百位结果。for k in range(m + n + 1):print('k:',k)# i是所有输出位,包括k=m+n,不包括m+n+1,其实就是遍历所有可能的num1和num2的单独一位,做一个总的累加# i、j他俩是严格针对k的互补关系。i = 时,j = 1;i = 1时,j = 0,他们都对应结果的“下标”k=1,也就是“十位”sum = carry#先把进位计算进来(这个顺序其实无所谓,但是如果不是先进位,就要给sum清零了)for i in range(k + 1):#k其实就是结果位。i和j是根据k做的互补,严格对应一个结果底位。j = k - iif i <= m and j <= n:index_i = m - i# 转换,字符串形式,i=0其实代表的是最大的那个数,不是最小的,index_i才是最小的数。index_j = n - jsum += int(num1[index_i]) * int(num2[index_j])## 拼接结果字符串,遍历完当前k对应的所有i和j的组合,当前位的结果已经出炉,可以拼接了。比如15*15的最后一位5*5,是由当前位停留结果5和进位2组成的,当前结果就留在这。res = str(sum % 10) + res#从低位向高位迭代,使用新的sum模,后加res的拼接方式。carry = sum // 10#进位,5*5=25,进位2if carry:#最后一位了,k迭代的是乘法计算,当然可能发生进位,比如33*44中,k是0到2,最高位3*4肯定要进位的res = str(carry) + resreturn res

方案4

参考烧脑版的第二个直观优雅的思路,进行python实现:先乘,再进位,代码如下:

# sol4:beat 34%
# 参考烧脑版的第二个思路进行python实现:先乘,再进位
class Solution:def multiply(self, num1: str, num2: str) -> str:# corner caseif num1 == "0" or num2 == "0":return "0"elif num1 == "1":return num2elif num2 == "1":return num1# common caselen1 = len(num1)len2 = len(num2)len_sum = len1 + len2# 从低位往高位相互进位,个、十、百、千、……n1_rev = num1[::-1]n2_rev = num2[::-1]multi_res_list = []# 整体乘完放1个数组num_rev_list = [0]*(len_sum)for i in range(len1):for j in range(len2):n1 = int(n1_rev[i])n2 = int(n2_rev[j])num_rev_list[i + j] += n1 * n2base = 10# 统一处理进制问题for i in range(len(num_rev_list) - 1):num_rev_list[i+1] += num_rev_list[i] // basenum_rev_list[i] = num_rev_list[i] % base# 处理最高位的corner case# if (num_rev_list[len_sum - 1] == 0):multi_res_list = num_rev_list[::-1]non_zero_idx = 0for val in multi_res_list:if (val == 0):non_zero_idx += 1else:breakmulti_res_list = multi_res_list[non_zero_idx:]multi_res_str = "".join((str(i) for i in multi_res_list)) # 列表中的每个数字转字符串return multi_res_str

无测试套主调

无测试套版本主调:

# 无测试套版本主调
if __name__ == '__main__':print('start!')# num1 = "2"# num2 = "3"# ret = "6"# num1 = "99"# num2 = "99"# ret = "9801"num1 = "10"num2 = "10"ret = "100"# num1 = "1"# num2 = "123456789"# ret = "123456789"# num1 = "123456789"# num2 = "0"# ret = "0"# num1 = "123"# num2 = "456"# ret = "56088"# num1 = "37689269854"# num2 = "12548698156"# ret = "472951271117876189224"# num1 = "6"# num2 = "501"# ret = "3006"problem = Solution()res = problem.multiply(num1, num2)assert(res == ret)print(res, "right!")print('done!')

测试套主调

编写含单元测试的主调:

# 导入单元测试
import unittest# function...# 编写测试套
class TestSol(unittest.TestCase):def test_bound1(self):num1 = "2"num2 = "3"ret = "6"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)def test_bound2(self):num1 = "37689269854"num2 = "12548698156"ret = "472951271117876189224"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)def test_bound3(self):num1 = "1"num2 = "123456789"ret = "123456789"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)def test_bound4(self):num1 = "123456789"num2 = "0"ret = "0"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)def test_special1(self):num1 = "6"num2 = "501"ret = "3006"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)def test_special2(self):num1 = "10"num2 = "10"ret = "100"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)def test_special3(self):num1 = "99"num2 = "99"ret = "9801"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)def test_common_case(self):num1 = "123"num2 = "456"ret = "56088"sol = Solution()self.assertEqual(sol.multiply(num1, num2), ret)# 含测试套版本主调
if __name__ == '__main__':print('start!')unittest.main() # 启动单元测试print('done!')

本文小结


为便于深入理解进制转换和乘法原理,同时提高编程能力,demo程序中新增单元测试代码实现。

卡壳点:

  1. 陷入复杂算法细节,而不是以终为始。在没明确思路前,先实现再优化,用笨办法/暴力法解决了,再尝试改进。
  2. corner case处理不当。 结尾中,输出字符串前导0场景。 中间乘积结果为0,进位符为0场景未考虑周全。

总的来说,推荐solution4方法进行解题。

此外,进阶想一想,如果将其变成大数加法,这个程序能否只改两三行代码,即可输出正确结果?再如,改成八进制乘法,如何搞?

题解参考

  • 暴力法分解为单数相乘:LeetCode:43 multiply 大数乘法的数学直观理解
  • 暴力法分解为字符串相加:字符串相乘(大数相乘、相加)
  • 进阶烧脑版高效算法实现:博客园版本、CSDN版本

涉及知识点

  • python 纯数字list转化为字符串,link
  • Python字符串中添加、插入特定字符,link
  • Python 列表逆序排列的 3 种方式,link
  • 廖雪峰,Python自带单元测试,unittest

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/53677.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学术会议征稿】2024年智能驾驶与智慧交通国际学术会议(IDST 2024)

2024年智能驾驶与智慧交通国际学术会议(IDST 2024) 2024 International Conference on Intelligent Driving and Smart Transportation 智能驾驶和智慧交通利用新兴技术&#xff0c;使城市出行更加方便、更具成本效益且更安全。在此背景下&#xff0c;由中南大学主办的2024年…

LLMs技术 | 整合Ollama实现本地LLMs调用

前言 近两年AIGC发展的非常迅速&#xff0c;从刚开始的只有ChatGPT到现在的很百家争鸣。从开始的大参数模型&#xff0c;再到后来的小参数模型&#xff0c;从一开始单一的文本模型到现在的多模态模型等等。随着一起进步的不仅仅是模型的多样化&#xff0c;还有模型的使用方式。…

65、Python之函数高级:装饰器实战,通用日志记录功能的动态添加

引言 从系统开发的规范性来说&#xff0c;日志的记录是一个规范化的要求&#xff0c;但是&#xff0c;有些程序员会觉得麻烦&#xff0c;反而不愿意记录日志&#xff0c;还是太年轻了…… 其实&#xff0c;如果个人保护意识稍微强一些&#xff0c;一定会主动进行日志的记录的…

python_openCV_计算图片中的区域的黑色比例

希望对原始图片进行处理&#xff0c;然后计算图片上的黑色和白色的占比 上图&#xff0c; 原始图片 import numpy as np import cv2 import matplotlib.pyplot as pltdef cal_black(img_file):#功能&#xff1a; 计算图片中的区域的黑色比例#取图片中不同的位置进行计算&…

关于武汉芯景科技有限公司的IIC缓冲器芯片XJ4307开发指南(兼容LTC4307)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.总线超时&#xff0c;自动断开连接 当 SDAOUT 或 SCLOUT 为低电平时&#xff0c;将启动内部定时器。定时器仅在相应输入变为高电平时重置。如果在 30ms &#xff08;典型值&#xff09; 内没有变为高…

国产芯片LT9211D:MIPI转LVDS转换器,分辨率高达3840x2160 30Hz,碾压其它同功能芯片

以下为LT9211D&#xff1a;MIPI TO LVDS的芯片简单介绍&#xff0c;供各位参考 Lontium LT9211D是一款高性能MIPI DSI/CSI-2到双端口LVDS转换器。LT9211D反序列化 输入MIPI视频数据&#xff0c;解码数据包&#xff0c;转换格式化的视频数据流到LVDS发射机输出AP与移动显示面板或…

基于STM32L431小熊派设计的智能花盆(微信小程序+腾讯云IOT)(223)

文章目录 一、前言1.1 项目介绍【1】项目背景【2】设计实现的功能【3】项目硬件模块组成1.2 设计思路【1】整体设计思路【2】ESP8266工作模式配置1.3 项目开发背景【1】选题的意义【2】可行性分析【3】参考文献1.4 开发工具的选择【1】设备端开发【2】上位机开发1.5 系统框架图…

ppt模板简约下载哪个?这些模板简约又大气

中秋节&#xff0c;作为中国传统节日中最具诗意的一个&#xff0c;月圆人团圆的美好寓意总是让人心生向往。 想在国际网站上宣传这一传统节日的独特魅力&#xff0c;却担心自己的PPT不够吸引人&#xff1f;别急&#xff0c;使用精美免费的ppt模板&#xff0c;可以让你的演示瞬…

创新性处理Java编程技术问题的策略

在Java编程领域&#xff0c;解决技术问题的方式不断进化。本文将探讨一些创新性和针对性的技术问题处理方法&#xff0c;帮助开发者高效地应对挑战&#xff0c;提高代码质量和开发效率。 1. 动态代理与反射机制的优化 Java的动态代理和反射机制为程序员提供了强大的功能&#…

【性能】DJANGO + REDIS 缓存提速

不加REDIS缓存时&#xff0c;每次访问都要读取数据库&#xff0c;当访问量非常大的时候&#xff0c; 就会有很多次的数据库查询&#xff0c;会造成访问速度变慢&#xff0c;服务器资源占用较多等问题。 当使用了缓存后&#xff0c;访问情况变成了如下&#xff1a;访问一个网址时…

用户登录和注销

在Linux系统中&#xff0c;用户登录和注销是一个常见的操作&#xff0c;涉及到用户账户管理和服务管理等多个方面。下面分别介绍用户在图形界面和命令行下的登录和注销流程。 图形界面下的登录和注销 登录 登录界面&#xff1a; 当用户启动计算机时&#xff0c;通常会看到一…

Python Flask_APScheduler定时任务的正确(最佳)使用

描述 APScheduler基于Quartz的一个Python定时任务框架&#xff0c;实现了Quartz的所有功能。最近使用Flask框架使用Flask_APScheduler来做定时任务&#xff0c;在使用过程当中也遇到很多问题&#xff0c;例如在定时任务调用的方法中需要用到flask的app.app_context()时&#…

无影云电脑:在最破的电脑上玩最顶配的游戏

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点 我对云电脑很感兴趣&#xff0c;这几天我深度体验了无影云电脑的个人版.&#xff0c;我给大家分享下。这款云电脑到底能不能替代你的笔记本?到底能不能改变人们使用电脑的方式? 先说结论&#xff1a; (1)从草根创…

【Canvas与艺术】菊花孔雀螺旋

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>菊花孔雀螺旋</title><style type"text/css">…

vue3实现打飞机(雷电)

代码可直接运行直接玩&#xff0c;而且要自己加上一些随机事件都很简单了&#xff08;例如发射速度变快&#xff0c;子弹变大&#xff0c;敌人变慢等&#xff09; <template><div class"flex items-center justify-center h-100vh w-full"><div>S…

.net MAUI应用生命周期

.NET Multi-platform App UI (.NET MAUI) 应用通常有四种执行状态&#xff1a;“未运行”、“运行中”、“已停用”和“已停止”。 当应用从未运行状态转换为运行状态、从运行状态转换为已停用状态、从已停用状态转换为已停止状态、从已停止状态转换为运行状态&#xff0c;以及…

【Kubernetes】K8s 的鉴权管理(二):基于属性 / 节点 / Webhook 的访问控制

K8s 的鉴权管理&#xff08;二&#xff09;&#xff1a;基于属性 / 节点 / Webhook 的访问控制 1.基于属性的访问控制&#xff08;ABAC 鉴权&#xff09;2.基于节点的访问控制&#xff08;node 鉴权&#xff09;2.1 读取操作2.2 写入操作 3.基于 Webhook 的访问控制3.1 基于 We…

替换cython_bbox库中bbox_ious

说明一下问题&#xff1a;目标追踪代码里往往用到cython_bbox中的bbox_ious。但是该库需要用到 VC&#xff0c;按照有些麻烦。于是采用直接替换该方法&#xff0c;用纯代码实现&#xff0c;无需调用库。 File “src\cython_bbox.pyx”, line 17, in cython_bbox.bbox_overlaps…

深度学习-01 Pytorch

torchvision是一个用于计算机视觉任务的Python包&#xff0c;它是PyTorch的一个扩展库。它提供了一些流行的数据集、模型架构和图像转换函数&#xff0c;以方便用户进行计算机视觉任务的开发和研究。 1.torchvision中包含了许多常用的计算机视觉数据集&#xff0c;如MNIST、CIF…

【AcWing】861. 二分图的最大匹配(匈牙利算法)

匈牙利算法&#xff0c;他可以在比较快的时间复杂度之内告诉我们左边和右边成功匹配的最大数是多少 匹配指的是边的数量&#xff0c;成功的匹配指的是两个未被使用的点之间存在一条边(就不存在两条边共用了一个点的)。 匈牙利算法可以返回成功匹配的最大匹配数是多少。 #incl…