《C语言程序设计:问题与求解方法》——3.4节验证算法的方法

简介:

本节书摘来自华章社区《C语言程序设计:问题与求解方法》一书中的第3章,第3.4节验证算法的方法,作者:何 勤,更多章节内容可以访问云栖社区“华章社区”公众号查看

3.4 验证算法的方法
前面说过,找到求解问题的算法对解决编程问题至关重要。但如何验证算法的正确性呢?本节将介绍采用画变量(内存单元)取值变化图来验证算法的方法(参见对例题3.3的算法验证),这也是成为程序员或编程高手的一种极为重要的技能。采用这种方法有两个好处:
1)大大减少不必要的编程工作量。在写出算法而尚未编出程序时,就可以检查算法是否存在逻辑错误或边界错误(数组的边界错误,或者循环次数多一次或少一次)、程序的流程是否构造正确等,从而减少了不必要的编程工作量。这类似于在装修前画出立体效果图,让用户感受一下在其中生活是否会感到舒服一样。如果用户感觉不好,可重新修改设计,大大减少了实际装修完后才返工的极大损失。毕竟重写和修改伪代码要比重写或修改程序省事多了—尤其是对一个大型程序。
2)可使读者读懂较难程序或算法的能力得到极大的提高。而阅读程序和算法的能力,是一个人能够否成为编程高手的最重要的、决定性因素之一。
希望读者至少用这种方法来分析和阅读几十个你认为比较难懂而又比较有趣的算法或程序,使之成为你的编程工具箱中的一件“利器”,并且养成一种良好的编程习惯。在用伪代码写出算法后,不要急于把它转换成高级语言的程序,而是用这种在纸面上运行、走查的方法去验证算法,尽可能在早期找出并修改算法中的逻辑错误或边界错误。
当然,即使通过这种验证,也不能保证这个算法是完全正确和完善的。对于语法错误更是无能为力。但这种验证的重要价值,我们绝对不能否认。
如果不用此方法来进行验证,在写出算法后就立即编程。仅仅依赖于运行编译程序来进行简单调试,你很可能最终会得到一个没有任何语法错误和运行时错误的程序。然而,此程序得到的结果要么可能是完全不正确的(逻辑错),要么此程序存在边界错误。如果是逻辑错误,则整个编程和调试工作完全是白费力气。在编写大型程序时,这种时间的浪费是一个很严重的问题。
【例题3.3】将一个三位整数反向后输出。(类型:必修题;趣味性:;难度:*)
一级算法:
1 输入一个三位整数→n
2 通过利用学过的算术运算符整除 / 和取余数 % 进行分解,分别求出此三位整数n的百位数n3,十位数n2和个位数n1
3 反向后的三位整数为 num=n1100+n210+n3
4 输出此三位数
其中第2步由于不能立即写出C语言的语句,需要进一步求精:
2.1 求出n的百位数:n3=n/100
2.2 求出n的十位数:n2=(n-n3*100)/10
2.3 求出n的个位数:n1=n%10
算法验证:执行算法第1步,假设将一个任意指定的三位整数(比如315)输入变量n中,用于验证上述算法过程的正确性。执行算法第1步后,变量的内存单元取值将如图3-1所示。

 n3    n2    n1    num

315 不确定 不确定 不确定 不确定


173c5d8ee560b179ecd94bb518dc12aa3f82842d

执行算法2.2步的赋值语句n2=(n–n3100)/10。先计算表达式(n–n3100)/10,将n和n3的当前值从内存中取出代入表达式,进行表达式规定的运算:(315–3*100)/10,得到结果1 ,存入变量n2中。算法2.2步执行完后,变量的内存单元取值将如图3-3所示(变量n2的值发生了变化)。
n n3 n2 n1 num
315 3 1 不确定 不确定


3cff271bd4de9e52861dbdd00277152188195539

经过以上验证,315确实变成了513,算法逻辑上的确是正确的。
将上述经过纸面运行走查验证的算法,转化为C语言程序如下:

1  #include<stdio.h>
2  int  main(void)
3  {
4   int  n;/*输入变量*/    
5   int  n3,n2,n1;/*中间变量*/
6   int  num; /* 结果变量*/
7   /*输入一个三位整数n*/
8   printf("请输入一个三位整数\n");  
9   scanf("%d", &n);  
10
11  /*分别求出它的百位数n3,十位数n2,个位数n1*/
12   n3=n/100;            
13   n2=(n-n3*100)/10; 
14   n1=n%10;          
15
16  /*反向后的三位整数为*/
17  num=n1*100+n2*10+n3;  
18
19  /*输出此三位数*/
20  printf("num=%d",num);      
21  return 0;         
22 }
【问题1】是否可以将第17行和第20行合并为下面这一句并删掉第6行?
printf("num=%d", n1*100+n2*10+n3);

答:可以。printf()函数调用时,格式串后的输出项可以是一个表达式。
【问题2】是否可以将第12行和第13行颠倒语句次序?用画变量内存单元取值变化图的方法验证你的结论。
答:不可以。变量n3会出现初始化错误。第13行表达式中的n3依赖第12行的语句初始化。
【问题3】是否可以将第13行与第14行互换?
答:可以。因为两条赋值语句之间的变量并没有依赖关系。
【问题4】是否可以将所有变量n,n3,n2,n1都用float型来定义?
答:不可以。程序第12行、第13行的赋值语句的表达式,都要求是整除运算。第14行的n%10,运算符 % 也要求变量n是整型,而不是实型。

相关文章
|
2月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
45 3
|
29天前
|
存储 算法 数据管理
C语言算法复杂度
【10月更文挑战第20天】
C语言算法复杂度
|
2月前
|
监控 算法 数据安全/隐私保护
基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
本项目展示了基于FPGA与MATLAB实现的三帧差算法运动目标检测。使用Vivado 2019.2和MATLAB 2022a开发环境,通过对比连续三帧图像的像素值变化,有效识别运动区域。项目包括完整无水印的运行效果预览、详细中文注释的代码及操作步骤视频,适合学习和研究。
|
2月前
|
算法 索引
HashMap扩容时的rehash方法中(e.hash & oldCap) == 0算法推导
HashMap在扩容时,会创建一个新数组,并将旧数组中的数据迁移过去。通过(e.hash & oldCap)是否等于0,数据被巧妙地分为两类:一类保持原有索引位置,另一类索引位置增加旧数组长度。此过程确保了数据均匀分布,提高了查询效率。
39 2
|
2月前
|
搜索推荐 Shell
解析排序算法:十大排序方法的工作原理与性能比较
解析排序算法:十大排序方法的工作原理与性能比较
54 9
|
2月前
|
Java 编译器 C语言
【一步一步了解Java系列】:Java中的方法对标C语言中的函数
【一步一步了解Java系列】:Java中的方法对标C语言中的函数
26 3
|
2月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
71 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
2月前
|
存储 算法 C语言
【C语言】二分查找算法
【C语言】二分查找算法
|
4月前
|
JavaScript 算法 前端开发
JS算法必备之String常用操作方法
这篇文章详细介绍了JavaScript中字符串的基本操作,包括创建字符串、访问特定字符、字符串的拼接、位置查找、大小写转换、模式匹配、以及字符串的迭代和格式化等方法。
JS算法必备之String常用操作方法
|
4月前
|
JavaScript 算法 前端开发
JS算法必备之Array常用操作方法
这篇文章详细介绍了JavaScript中数组的创建、检测、转换、排序、操作方法以及迭代方法等,提供了数组操作的全面指南。
JS算法必备之Array常用操作方法