程序复杂度之圈复杂度

简介:

圈复杂度(Cyclomatic complexity)也称为条件复杂度或循环复杂度,是一种软件度量,是由Thomas J. McCabe, Sr. 在 1976 年提出,用来表示程序的复杂度,其符号为 VG 或是 M。圈复杂度是对源代码中线性独立路径数的定量测量。

圈复杂度使用的程序的控制流图来计算:在图中的节点对应于程序中一组不可分割的命令[代码行],有向边连接两个可连续执行的节点;[可连续执行的两个节点:第二个节点的命令组可能在第一个节点执行后立刻开始执行]。圈复杂度可以应用到独立的功能,模块,方法或类。

基础路径测试:通过测试用例测试程序中的每个线性无关的独立路径;在这种测试策略下,测试用例的数目将等于该程序的圈复杂度;

圈复杂度定义

圈复杂度度量的是程序中线性独立路径的数量;例如:如果程序中不包含控制、判断、条件语句(例如 if,swith 等),那么复杂度就是 1 ;因为整个程序只有一条执行路径;如果程序包含一条IF语句,那么就会有两条路径来执行完整个程序(IF为 TRUE,IF 为 FALSE),所以这时候的复杂度就是 2;两个嵌套的 IF 语句,或者包含两个判断条件的一个 IF 语句,复杂度就是 4;

在数学上,一个结构化程序的圈复杂度通过该程序的控制流图来定义;控制流图包含程序的基本块(图的节点),和两个基本块之间可执行性(图的边)。

原理:

上图:单程序的控制流图。此程序由红色的节点开始运行,然后进入循环(红色节点下由三个节点组成),离开循环后有条件分支,最后运行蓝色节点后结束,此控制流图中,E = 9, N = 8, P = 1,因此其圈复杂度为 9 - 8 + (2*1) = 3

数学表达式:

                    M = E - N + 2P

其中
E 为图中边的个数
N 为图中节点的个数
P 为连接组件的个数

强连通图的圈复杂度定义:要求做完线上功能的先要求线上的功能

上图:对应同一个程序的控制流图,但多加一个从结束点到启始点的边,因此为强连通的控制流图,若利用此图计算圈复杂度,其公式为 M=E-N+P,而 E = 10、N = 8、P = 1,因此圈复杂度为 3
另一个计算圈复杂度的公式用与计算每一个结束节点总是连接到启动节点的流程控制图;这种图称为强连通的;此时的圈复杂度就是图中回路的个数,也称为第一贝蒂数[first Betti number]),其公式如下:

                      M = E - N + P

上式可以视为计算图中线性独立回路(回路内不包括其他回路)的个数,由于控制流图增加结束点到启始点的边,因此对应一个结束点至少会有一个回路。
对于单一的程序(或副程序或方法),P 恒为 1。对于单一子程序,该公式可以简单的表达为:

                      M = E - N + 2

圈复杂度可以适用于同时分析许多程序或副程序的情形(例如针对一个类中的所有方法),此时 P 等于程序的个数,因为每一个程序的图都是一个独立的连接组件。若每一个程序都只一个结束点,P 也可以视为是结束点的个数。

McCache 证明了任何只有一个进入点和一个技术点的结构化程序,其圈复杂度等于程序中决策点(IF,FOR loops)的个数加 1;

圈复杂度也可以延伸到多个结束点的程序,此时的圈复杂度如下:


                       π - s + 2

其中
π 是程序中决策点的个数

s 为结束点的个数

圈复杂度应用

限制软件复杂度
麦凯布提出圈复杂度时,其原始目的之一就是希望在软件开发过程中就限制其复杂度。他建议程序设计者需计算其开发模块的复杂度,若一模块的圈复杂度超过 10,需再分区为更小的模块。NIST(国家标准技术研究所)的结构化测试方法论已此作法略作调整,在一些特定情形下,模块圈复杂度上限放宽到 15 会比较合适。此方法论也承认有些特殊情形下,模块的复杂度需要超过上述的上限,其建议为“模块的圈复杂度需在上限范围以内,否则需提供书面数据,说明为何此模块圈复杂度有必要超过上限。”

评估软件的结构化程度 「structuredness」

Essential complexity (numerical measure of "structuredness")

启示软件测试工作

圈复杂度决定了需要多少个测试用来达到特定模块测试覆盖率要求;

模块内聚性的评估
可以预期一个复杂度较高模块的内聚性会比较低,至少不会到功能内聚性的程度。一个有高复杂度及低内聚性的模块中会有许多的决策点,这类的模块多半运行超过一个明确定义的任务,因此内聚性较低。一个 2005 年的研究发现复杂度的度量和由专家评估的模块内聚性有高度负相关,反而针对内聚性设计的度量和专家评估结果之间的相关性还比较不明显[6]。

推测软件缺陷个数

许多研究指出一模块及方法的圈复杂度和其中的缺陷个数有相关性,许多这类研究发现圈复杂度和缺陷个数有高度的正相关:圈复杂度最高的模块及方法,其中的缺陷个数也最多。

不过,有些研究是在控制模块大小相近的情形下进行分析(例如比较二个源代码行数相近,但圈复杂度不同模块的缺陷个数),许多这类的研究发现圈复杂度和缺陷个数没有明显相关,不过仍有一些研究认为在此情形下二者仍有相关性。有些此领域的研究者认为那些研究结果圈复杂度和缺陷个数没有明显相关的研究,其研究方法的有效性可能有问题。

莱斯·哈顿认为利用圈复杂度来预测缺陷个数,和利用源代码行数来预测缺陷个数的结果大致相近。

OneAPM Mobile Insight 以真实用户体验为度量标准进行 Crash 分析,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客
本文转自 OneAPM 官方博客

相关文章
|
1月前
|
机器学习/深度学习
复杂度练习
复杂度练习
|
1月前
模块功能降低复杂度
模块功能降低复杂度
14 3
|
5月前
|
缓存 分布式计算 并行计算
平摊复杂度
平摊复杂度(Amortized Complexity)是一种在计算复杂度时使用的技术,用于描述算法在多次运行中的平均性能。平摊复杂度能够将一次性计算的复杂度分摊到多次运行中,从而更准确地衡量算法在实际应用中的性能。
31 3
|
6月前
|
存储 算法
【算法的复杂度】
【算法的复杂度】
25 0
|
6月前
|
算法
算法中的复杂度
算法中的复杂度
28 0
|
7月前
|
存储 算法 数据库
算法:复杂度
算法:复杂度
49 0
|
10月前
|
搜索推荐 算法
认识复杂度和简单排序算法
认识复杂度和简单排序算法
59 0
|
算法 程序员
常见代码复杂度解析
代码质量评价维度,很多都是些主观性的评价维度,需要有专门的人员去查看评判代码,对于审核的人员代码能力要求比较高,而且有时候往往不同的人审核会得出不同的结论,会有争议。然而也有些对代码客观的分析方式可以帮助我们识别代码质量,节省大量人力去分析代码。比如代码复杂度的分析。
2207 0
|
存储 算法
算法学习 | 加深了解算法的复杂度
本篇从时间复杂度和空间复杂度出发,深入了解一下算法的复杂性。
126 1
|
机器学习/深度学习 算法 C语言
[最全算法总结]我是如何将递归算法的复杂度优化到O(1)的
[最全算法总结]我是如何将递归算法的复杂度优化到O(1)的
144 0
[最全算法总结]我是如何将递归算法的复杂度优化到O(1)的