文章目录

  • 从一道动规看`raise`的常见用法
    • 粗略编写算法
    • 出错
    • 用`raise`传递信息
    • `raise`的用法总结
  • 条件断点
  • 总结和问答练习

时效性
本篇撰写时间为2021.11.14,由于计算机技术日新月异,博客中所有内容都有时效和版本限制,具体做法不一定总行得通,链接可能改动失效,各种软件的用法可能有修改。但是其中透露的思想往往是值得学习的。
Windows 10家庭中文版,版本20H2,操作系统内部版本19042.1288
本篇前置:

  • ExpRe[4] python[1] 单元测试,算法题对拍
    https://www.cnblogs.com/minor-second/p/15549364.html
  • 大致知道python3中的异常,try机制

从一道动规看raise的常见用法

我们继续做算法设计与分析作业

考虑一个所有元素为正的 n ∗ n n*n n∗n二维数组(邻接矩阵),已知其中有且仅有一条有向回路使得该回路上各权值乘积大于1,试找出该回路。(特别注意可以自己到自己连边,且这样的边也有可能权重大于1)

其实括号提示是我自己加的。有点坑。

粗略编写算法

我们粗略编写如下类似于Floyd算法的程序

def find_loop(n, mat):max_mat = []for i in range(n):max_mat.append(mat[i].copy())for j in range(n):max_mat[i][j] = [max_mat[i][j], -1]loop_start = 0try:for k in range(n):for i in range(n):for j in range(n):if max_mat[i][k][0] * max_mat[k][j][0] > max_mat[i][j][0]:max_mat[i][j][0] = max_mat[i][k][0] * max_mat[k][j][0]max_mat[i][j][1] = kif i==j and max_mat[i][j][0] > 1:loop_start = iraise StopIterationexcept StopIteration:seq = [loop_start, loop_start]def insert(i,max_mat,seq):s, e = seq[i], seq[i+1]if max_mat[s][e][1] == -1:return seqseq = seq[:i+1] + [max_mat[s][e][1]] + seq[i+1:]seq = insert(i+1,max_mat,seq)seq = insert(i,max_mat,seq)return seqreturn insert(0,max_mat,seq)
  • 首先深度拷贝输入max_mat防止改变输入对象(以便于测试)。并把原先矩阵中单独的数改成两个元素的表,表的第0个分量为权重(或多个权重乘积),第1个分量为“插入的点”。
  • 使用类似Floyd算法的三重循环结构,最外层是依次考察“插入这个点是否变好”。先考察“ i → j i\to j i→j改成 i → 0 → j i\to 0\to j i→0→j是否变好”等 n 2 n^2 n2个命题,再考察“ i → j i\to j i→j改成 i → 1 → j i\to 1\to j i→1→j是否变好”等 n 2 n^2 n2个命题。考察完至多全部 k k k值共 n 3 n^3 n3个命题后就找到了回路。
    其中max_mat[i][j][1] = k语句就是存储插入的点,方便最后回溯得到路径。
  • 为了得到路径,我们先得到一个路径上的点loop_start,然后递归地利用矩阵中记录的信息向其中插入中间节点。
    insert使用了递归,因此效率不会很高(当然都用python写算法题了也不在乎卡常了是吧)。
    insert函数有个细节是先插入i+1处,再插入i处,否则显然导致错误结果。

出错

我们编写一个单元测试看看该算法对不对。
文件树

test2.py中,输入代码

import unittest
import random
from problem2 import find_loop
class Test(unittest.TestCase):def test_auto(self):for _ in range(100):mat = list(list(random.random() * 0.8 for i in range(10)) for i in range(10))l = [i for i in range(10)]random.shuffle(l)n = random.randrange(1, 11)l = l[:n]pair_list = list((l[i-1], l[i]) for i in range(len(l)))for pair in pair_list:MAGIC = 0.9999mat[pair[0]][pair[1]] = MAGIClast_pair = pair_list[-1]mat[last_pair[0]][last_pair[1]] *= MAGIC**(-len(l)-1)seq = find_loop(10, mat)self.assertIn(l[0], seq)seq = seq[:-1] * 2for i in range(len(seq)):if seq[i] == l[0]:self.assertEqual(seq[i:i+len(l)],l)breakif __name__ == '__main__':unittest.main()
  • 其思路是先生成元素都在 0 0 0至 0.8 0.8 0.8的10阶方阵,然后随机生成一个不重复的整数序列l(使用了shuffle打乱)。
  • 接着,例如对于序列[1, 2, 3],生成二元组序列[(3,1), (1,2), (2,3)](特别注意负数下标使用),并人工改变这些序列对应的邻接矩阵中的权值,造出一条权重乘积大于1的回路。
  • 调用算法得到的seq未必和l相同。比如seq = [1,2,3,1]l = [3,1,2]. 但我们只需把seq去掉末尾元再重复2遍,其中就一定包含l为“子串”了。

结果是有时能通过测试有时不能。试图改变循环次数100为更大或更小的数值,发现有一个概率量级为 1 0 − 2 ∼ 1 0 − 1 10^{-2} \sim 10^{-1} 10−2∼10−1的错误。(回忆 ( 1 − 1 / n ) n ≈ 1 / e (1-1/n)^n\approx 1/e (1−1/n)n≈1/e)
报错RecursionError: maximum recursion depth exceeded in comparison.

raise传递信息

我们用try包裹出错的seq = find_loop(10, mat)语句

            try:seq = find_loop(10, mat)except RecursionError:raise RecursionError(seq, l, mat)

多次运行,发现输出的共同特点是l == [0]. 这就方便找到错误了:当0到0路径权重大于1时,我们在0到0中间插入了点0,从而导致无限循环。(有趣的是,l == [1]不会发生此错误。因为此时在外层循环到k==1之前就已经能发现1到1的权重大于1了)

raise的用法总结

  • 用法之一:可以看到我们使用try包裹代码段,并用raise手动引发异常,用except捕捉直接跳出多重循环。
  • 用法之二:在异常后加括号,其中添加任意多个参数,从而传递信息。

条件断点

对于新手(其实就是我),即使知道错误都出现在l == [0],也未必能马上看出错误。此时可能需要打条件断点方便调试。

  • 错误做法:
if 条件:pass

pass处加断点可能无法起作用。因为pass语句作为占位符,可能不对应任何实际编译出的代码,直接被“忽略”。

  • 一种比较丑陋的做法是把pass改成...(即所谓ellipsis)或0这种无作用的表达式,从而可加断点。
  • 而比较好的做法是在VSCode中原本加断点的位置右键(如图),使用VSCode的条件断点功能。典型的是当满足某条件时中断。当然也有其它更强大的功能。
  • 当然还可以使用高版本python的breakpoint()函数。

总结和问答练习

  1. Q: 像本文中一样用异常跳出多重循环可能有什么坏处?
    A: 比如因为其他原因引起StopIteration时也被文中的except捕捉了。
    为了解决这个可以自定义异常类型。
  2. Q: 从ExpRe[4]和[5],你对“生成测试数据”有何感想?
    A: 随机生成数据可能由于概率原因无法覆盖一些情况。
    对于输入有限制的情况(比如本题),如果随机生成数据可能会丢弃(浪费)大量数据(甚至有时检测是否符合约束本身就很费劲)。
    如果人工生成符合约束的数据往往费时费力且分布“过于单一”难以考察各种情况。
    因此使用约束求解等方法生成测试数据确实是有用的技术。
  3. Q: 解释文中错误发生的概率量级。
    A: 错误概率显然为 1 / 100 1/100 1/100(l为单元素,且恰好为[0], 1 / 10 ∗ 1 / 10 = 1 / 100 1/10*1/10 = 1/100 1/10∗1/10=1/100),因此循环100次通过的概率大致为 1 / e 1/e 1/e.
    实验证明,当单元测试中每次循环100次时,通过和不通过的情况大概在同一数量级。

ExpRe[5] python[2] raise语句,条件断点相关推荐

  1. python 使用raise语句主动抛出异常(Exception)、将异常抛出给上一级

    主动抛出异常 示例: 参考文章:使用Python提供的raise语句主动抛出异常 将异常抛出给上一级 示例: # -*- coding: utf-8 -*- """ @F ...

  2. python raise语句_Python异常处理,告别xxxxError!

    程序在运行的过程中,产生了异常,这时,我们可能会有两种想法,第一种是针对这个异常做某些特殊的处理来进行程序的降级处理:第二种是希望程序忽略这个异常继续执行下去,这个异常可能并不干扰主逻辑的执行. 那这 ...

  3. python raise语句_python中异常报错的分析处理

    想必到现在经过python基础的学习之后,小伙伴们都已经开始写很多脚本了,有大的有小的,但是有的时候并不是所写的能够顺利跑出结果来,期间会有不但的报错以及异常,很多我们都不理解,所以也就不会修改,这是 ...

  4. python中try...except的用法_python try...except语句、自定义异常、raise语句使用实例(异常处理的三种方法)...

    异常处理,在编程中是必不可少的.错误难免会发生,用户不可能完全按照开发者的意愿行事,也有一些不可预知的错误,如网络请求等. 而程序一旦遇到异常,就会被终止,并且由底层抛出错误栈,无法按照计划顺利执行. ...

  5. 系统学习Python——异常处理:raise语句

    如果要显式地触发异常,可以使用raise语句.它们的一般形式相当简单.一条raise语句的组成包括raise关键字,后面跟着可选的要引发的异常类或者异常类的一个实例: raise instance # ...

  6. python try exception类_Python异常-try、raise语句及自定义异常类

    一.try语句 #try/except try: pass except: pass try: pass except Exception: pass #try/except... try: pass ...

  7. python基本语法语句-python学习笔记:基本语法

    原标题:python学习笔记:基本语法 缩进:必须使用4个空格来表示每级缩进,支持Tab字符 if语句,经常与else, elif(相当于else if) 配合使用. for语句,迭代器,依次处理迭代 ...

  8. 深入理解Python的With-as语句

    学习Python有一段时间了,最近做一个项目会涉及到文件的读取和关闭.比如:我想把一些对象序列化到文件里面,然后当我再次使用的时候,在从文件里面读取反序列化成对象.像这种操作一般都是用try-exce ...

  9. python raise_python raise 使用方法

    是否可以在程序的指定位置手动抛出一个异常?答案是肯定的,Python 允许我们在程序中手动设置异常,使用 raise 语句即可. 读者可能会感到疑惑,即我们从来都是想方设法地让程序正常运行,为什么还要 ...

最新文章

  1. DailyTick 开发实录 —— UI 设计
  2. python怎么控制while循环_Python流程控制之while循环怎么学呢?老男孩Python
  3. sqlite java excel,Android将Excel表数据导入SQLite数据库
  4. linux 进程参数文件 /proc/pid/cmdline 简介
  5. curl 探测java网站_使用cURL查找网站重定向的位置?
  6. 区分 UML 类图中的几种关系
  7. mySQL的安装教程
  8. xxx定律-poj-3782
  9. 13-一对多左连接查询分步查询(查询所有客户及客户对应的订单)
  10. 125_Power BI 中 DAX 的性能测试
  11. sql截去最后一位_数据技能篇(EXCEL,SQL,Python)
  12. 在没有Docker容器的Ubuntu上安装SQL Server 2019
  13. android arm linux下使用内存转储crash工具分析 kernel system dump问题
  14. 【BZOJ5336】[TJOI2018]party(动态规划)
  15. 计算机的来源知识,计算机的由来计算机从诞生到现在才不过50多年的时间,可是发展却很快,已先后经历了四代,可以说是人丁兴旺。世界上第一台电子计算机1946年诞生于美国,名为埃尼阿克(ENIAC)...
  16. 计算机配置 主板,整套解决方案:I5-3470处理器和什么主板构成计算机配置?
  17. Python实现自由爆率抽奖小程序
  18. Linux各版本内核下载地址
  19. 【遇见Doris】Apache Doris 在京东广告平台的应用
  20. Chino with Rewrite

热门文章

  1. Python3爬虫图片抓取
  2. 卜若的代码笔记系列-unity系列-第三章:android交互之android studio(as)打jar包-5003
  3. gt和htd什么区别_同步带htd-3m和s3m区别是什么
  4. ▲ Android仿腾讯WiFi底部导航
  5. 笔记本清灰后组装后出现蓝屏,并不断的循环重启。
  6. Windows下安装 MongoDB
  7. 作为领导,如何既立威还不让下属反感?
  8. NVMe协议逻辑实现、nvme固态硬盘,支持master和slave两种模式,FPGA、SSD控制器,接口统一标准化、简单方便
  9. discuz要什么系统服务器,Discuz! Q安装,服务器要求详细说明
  10. 实验7-继承下的构造函数与析构函数